Files
SwiftLint/Source/SwiftLintFramework/Rules/DynamicInlineRule.swift
T
JP Simard b83e0991b9 Remove all file headers
The MIT license doesn't require that all files be prepended with this
licensing or copyright information. Realm confirmed that they're ok with this
change. This will enable some companies to contribute to SwiftLint and the
date & authorship information will remain accessible via git source control.
2018-05-04 13:42:02 -07:00

75 lines
3.2 KiB
Swift

import Foundation
import SourceKittenFramework
public struct DynamicInlineRule: ASTRule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.error)
public init() {}
public static let description = RuleDescription(
identifier: "dynamic_inline",
name: "Dynamic Inline",
description: "Avoid using 'dynamic' and '@inline(__always)' together.",
kind: .lint,
nonTriggeringExamples: [
"class C {\ndynamic func f() {}}",
"class C {\n@inline(__always) func f() {}}",
"class C {\n@inline(never) dynamic func f() {}}"
],
triggeringExamples: [
"class C {\n@inline(__always) dynamic ↓func f() {}\n}",
"class C {\n@inline(__always) public dynamic ↓func f() {}\n}",
"class C {\n@inline(__always) dynamic internal ↓func f() {}\n}",
"class C {\n@inline(__always)\ndynamic ↓func f() {}\n}",
"class C {\n@inline(__always)\ndynamic\n↓func f() {}\n}"
]
)
public func validate(file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
// Look for functions with both "inline" and "dynamic". For each of these, we can get offset
// of the "func" keyword. We can assume that the nearest "@inline" before this offset is
// the attribute we are interested in.
guard functionKinds.contains(kind),
case let attributes = dictionary.enclosedSwiftAttributes,
attributes.contains(.dynamic),
attributes.contains(.inline),
let funcByteOffset = dictionary.offset,
let funcOffset = file.contents.bridge()
.byteRangeToNSRange(start: funcByteOffset, length: 0)?.location,
case let inlinePattern = regex("@inline"),
case let range = NSRange(location: 0, length: funcOffset),
let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range)
.last,
inlineMatch.range.location != NSNotFound,
case let attributeRange = NSRange(location: inlineMatch.range.location,
length: funcOffset - inlineMatch.range.location),
case let alwaysInlinePattern = regex("@inline\\(\\s*__always\\s*\\)"),
alwaysInlinePattern.firstMatch(in: file.contents, options: [], range: attributeRange) != nil
else {
return []
}
return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, characterOffset: funcOffset))]
}
fileprivate let functionKinds: [SwiftDeclarationKind] = [
.functionAccessorAddress,
.functionAccessorDidset,
.functionAccessorGetter,
.functionAccessorMutableaddress,
.functionAccessorSetter,
.functionAccessorWillset,
.functionConstructor,
.functionDestructor,
.functionFree,
.functionMethodClass,
.functionMethodInstance,
.functionMethodStatic,
.functionOperator,
.functionSubscript
]
}