forked from realm/SwiftLint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DynamicInlineRule.swift
81 lines (74 loc) · 3.34 KB
/
DynamicInlineRule.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//
// DynamicInlineRule.swift
// SwiftLint
//
// Created by Daniel Duan on 12/08/16.
// Copyright © 2015 Realm. All rights reserved.
//
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.",
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("source.decl.attribute.dynamic"),
attributes.contains("source.decl.attribute.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
]
}