Skip to content

Commit

Permalink
Rewriting rule to deal with nested structures
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelofabri committed Nov 13, 2016
1 parent 3b68294 commit 772e702
Showing 1 changed file with 77 additions and 61 deletions.
138 changes: 77 additions & 61 deletions Source/SwiftLintFramework/Rules/ImplicitGetterRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private func classScoped(value: String) -> String {
return "class Foo {\n \(value)\n}\n"
}

public struct ImplicitGetterRule: ASTRule, ConfigurationProviderRule {
public struct ImplicitGetterRule: Rule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.Warning)

public init() {}
Expand All @@ -31,82 +31,51 @@ public struct ImplicitGetterRule: ASTRule, ConfigurationProviderRule {
classScoped("var foo: Int {\n return getValueFromDisk() \n} \n}"),
classScoped("var foo: String {\n return \"get\" \n} \n}"),
"protocol Foo {\n var foo: Int { get }\n",
"protocol Foo {\n var foo: Int { get set }\n"
"protocol Foo {\n var foo: Int { get set }\n",
"class Foo {\n" +
" var foo: Int {\n" +
" struct Bar {\n" +
" var bar: Int {\n" +
" get { return 1 }\n" +
" set { _ = newValue }\n" +
" }\n" +
" }\n" +
" return Bar().bar\n" +
" }\n" +
"}\n"
],
triggeringExamples: [
classScoped("var foo: Int {\n ↓get {\n return 20 \n} \n} \n}"),
classScoped("static var foo: Int {\n ↓get {\n return 20 \n} \n} \n}")
]
)

public func validateFile(file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
let typeKinds: [SwiftDeclarationKind] = [
.Class,
.Enum,
.Extension,
.ExtensionClass,
.ExtensionEnum,
.ExtensionProtocol,
.ExtensionStruct,
.Struct
]

guard typeKinds.contains(kind) else {
return []
}

guard let substructures = (dictionary["key.substructure"] as? [SourceKitRepresentable])?
.flatMap({ $0 as? [String: SourceKitRepresentable] }) else {
return []
}

return substructures.flatMap { dictionary -> [StyleViolation] in
guard let kind = (dictionary["key.kind"] as? String).flatMap(KindType.init) else {
return []
}

return validateType(file, kind: kind, dictionary: dictionary)
}
}

private func validateType(file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
let allowedKinds: [SwiftDeclarationKind] = [.VarClass, .VarInstance, .VarStatic]
guard allowedKinds.contains(kind) else {
return []
}

// If there's a setter, `get` is allowed
guard dictionary["key.setter_accessibility"] == nil else {
return []
}

// Only validates properties with body
guard let bodyOffset = (dictionary["key.bodyoffset"] as? Int64).flatMap({ Int($0) }),
bodyLength = (dictionary["key.bodylength"] as? Int64).flatMap({ Int($0) }) else {
return []
}

let bodyRange = NSRange(location: bodyOffset, length: bodyLength)
let contents = (file.contents as NSString)

let tokens = file.syntaxMap.tokensIn(bodyRange).filter { token in
public func validateFile(file: File) -> [StyleViolation] {
let getTokens = file.syntaxMap.tokens.filter { token -> Bool in
guard SyntaxKind(rawValue: token.type) == .Keyword else {
return false
}

guard let tokenValue = contents.substringWithByteRange(start: token.offset,
length: token.length) else {
return false
guard let tokenValue = file.contents.substringWithByteRange(start: token.offset,
length: token.length) else {
return false
}

return tokenValue == "get"
}

return tokens.map { token in
let violatingTokens = getTokens.filter { token -> Bool in
// the last element is the deepest structure
guard let dictionary =
variableDeclarationsFor(token.offset, structure: file.structure).last else {
return false
}

// If there's a setter, `get` is allowed
return dictionary["key.setter_accessibility"] == nil
}

return violatingTokens.map { token in
// Violation found!
let location = Location(file: file, byteOffset: token.offset)

Expand All @@ -116,4 +85,51 @@ public struct ImplicitGetterRule: ASTRule, ConfigurationProviderRule {
)
}
}

private func variableDeclarationsFor(byteOffset: Int, structure: Structure) ->
[[String : SourceKitRepresentable]] {
var results = [[String : SourceKitRepresentable]]()

func parse(dictionary: [String : SourceKitRepresentable]) {

let allowedKinds: [SwiftDeclarationKind] = [.VarClass, .VarInstance, .VarStatic]

// Only accepts variable declarations which contains a body and contains the
// searched byteOffset
if let kindString = (dictionary["key.kind"] as? String),
kind = SwiftDeclarationKind(rawValue: kindString),
bodyOffset = (dictionary["key.bodyoffset"] as? Int64).flatMap({ Int($0) }),
bodyLength = (dictionary["key.bodylength"] as? Int64).flatMap({ Int($0) })
where allowedKinds.contains(kind) {
let byteRange = NSRange(location: bodyOffset, length: bodyLength)

if NSLocationInRange(byteOffset, byteRange) {
results.append(dictionary)
}
}

let typeKinds: [SwiftDeclarationKind] = [
.Class,
.Enum,
.Extension,
.ExtensionClass,
.ExtensionEnum,
.ExtensionProtocol,
.ExtensionStruct,
.Struct
] + allowedKinds

if let subStructure = dictionary["key.substructure"] as? [SourceKitRepresentable] {
for case let dictionary as [String : SourceKitRepresentable] in subStructure {
if let kindString = (dictionary["key.kind"] as? String),
kind = SwiftDeclarationKind(rawValue: kindString)
where typeKinds.contains(kind) {
parse(dictionary)
}
}
}
}
parse(structure.dictionary)
return results
}
}

0 comments on commit 772e702

Please sign in to comment.