Skip to content

Commit

Permalink
Merge pull request #513 from realm/nn-handle-sourcekitd-error
Browse files Browse the repository at this point in the history
handle sourcekitd error
  • Loading branch information
jpsim committed Apr 14, 2016
2 parents 8fd171f + f5ee5ee commit 62c7d4a
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
[Norio Nomura](https://github.com/norio-nomura)
[#167](https://github.com/jpsim/SourceKitten/issues/167)

* SwiftLint no longer crashes when SourceKitService crashes.
[Norio Nomura](https://github.com/norio-nomura)

##### Bug Fixes

* Failed to launch swiftlint when Xcode.app was placed at non standard path.
Expand Down
84 changes: 74 additions & 10 deletions Source/SwiftLintFramework/Extensions/File+Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,29 @@
import Foundation
import SourceKittenFramework

private var responseCache = Cache({file in Request.EditorOpen(file).send()})
private var structureCache = Cache({file in Structure(sourceKitResponse: responseCache.get(file))})
private var syntaxMapCache = Cache({file in SyntaxMap(sourceKitResponse: responseCache.get(file))})
private var responseCache = Cache({file -> [String: SourceKitRepresentable]? in
do {
return try Request.EditorOpen(file).failableSend()
} catch let error as Request.Error {
queuedPrintError(error.description)
return nil
} catch {
return nil
}
})
private var structureCache = Cache({file -> Structure? in
if let structure = responseCache.get(file).map(Structure.init) {
queueForRebuild.append(structure)
return structure
}
return nil
})
private var syntaxMapCache = Cache({file in responseCache.get(file).map(SyntaxMap.init)})
private var syntaxKindsByLinesCache = Cache({file in file.syntaxKindsByLine()})

private typealias AssertHandler = () -> ()
private var assertHandlers = [String: AssertHandler?]()

private var _allDeclarationsByType = [String: [String]]()
private var queueForRebuild = [Structure]()

Expand All @@ -27,15 +45,12 @@ private struct Cache<T> {
}

private mutating func get(file: File) -> T {
let key = file.path ?? NSUUID().UUIDString
let key = file.cacheKey
if let value = values[key] {
return value
}
let value = factory(file)
values[key] = value
if let structure = value as? Structure {
queueForRebuild.append(structure)
}
return value
}

Expand All @@ -52,20 +67,68 @@ private struct Cache<T> {

extension File {

private var cacheKey: String {
return path ?? "\(ObjectIdentifier(self).hashValue)"
}

internal var sourcekitdFailed: Bool {
get {
return responseCache.get(self) == nil
}
set {
if newValue {
responseCache.values[cacheKey] = Optional<[String: SourceKitRepresentable]>.None
} else {
responseCache.values.removeValueForKey(cacheKey)
}
}
}

internal var assertHandler: (() -> ())? {
get {
return assertHandlers[cacheKey] ?? nil
}
set {
assertHandlers[cacheKey] = newValue
}
}

internal var structure: Structure {
return structureCache.get(self)
guard let structure = structureCache.get(self) else {
if let handler = assertHandler {
handler()
return Structure(sourceKitResponse: [:])
}
fatalError("Never call this for file that sourcekitd fails.")
}
return structure
}

internal var syntaxMap: SyntaxMap {
return syntaxMapCache.get(self)
guard let syntaxMap = syntaxMapCache.get(self) else {
if let handler = assertHandler {
handler()
return SyntaxMap(data: [])
}
fatalError("Never call this for file that sourcekitd fails.")
}
return syntaxMap
}

internal var syntaxKindsByLines: [[SyntaxKind]] {
return syntaxKindsByLinesCache.get(self)
guard let syntaxKindsByLines = syntaxKindsByLinesCache.get(self) else {
if let handler = assertHandler {
handler()
return []
}
fatalError("Never call this for file that sourcekitd fails.")
}
return syntaxKindsByLines
}

public func invalidateCache() {
responseCache.invalidate(self)
assertHandlers.removeValueForKey(cacheKey)
structureCache.invalidate(self)
syntaxMapCache.invalidate(self)
syntaxKindsByLinesCache.invalidate(self)
Expand All @@ -75,6 +138,7 @@ extension File {
queueForRebuild = []
_allDeclarationsByType = [:]
responseCache.clear()
assertHandlers = [:]
structureCache.clear()
syntaxMapCache.clear()
syntaxKindsByLinesCache.clear()
Expand Down
8 changes: 7 additions & 1 deletion Source/SwiftLintFramework/Extensions/File+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ extension File {
}

private func commands() -> [Command] {
if sourcekitdFailed {
return []
}
let contents = self.contents as NSString
return matchPattern("swiftlint:(enable|disable)(:previous|:this|:next)?\\ [^\\s]+",
withSyntaxKinds: [.Comment]).flatMap { range in
Expand Down Expand Up @@ -100,7 +103,10 @@ extension File {
}
}

internal func syntaxKindsByLine() -> [[SyntaxKind]] {
internal func syntaxKindsByLine() -> [[SyntaxKind]]? {
if sourcekitdFailed {
return nil
}
var results = [[SyntaxKind]](count: lines.count + 1, repeatedValue: [])
var tokenGenerator = syntaxMap.tokens.generate()
var lineGenerator = lines.generate()
Expand Down
7 changes: 7 additions & 0 deletions Source/SwiftLintFramework/Models/Linter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ public struct Linter {

private func getStyleViolations(benchmark: Bool = false) ->
([StyleViolation], [(id: String, time: Double)]) {
if file.sourcekitdFailed {
queuedPrintError("Most of rules are skipped because sourcekitd fails.")
}
let regions = file.regions()
var ruleTimes = [(id: String, time: Double)]()
let violations = rules.flatMap { rule -> [StyleViolation] in
if (rule.dynamicType.description.needsSourceKit || rule is ASTRule) &&
self.file.sourcekitdFailed {
return []
}
let start: NSDate! = benchmark ? NSDate() : nil
let violations = rule.validateFile(self.file)
if benchmark {
Expand Down
4 changes: 3 additions & 1 deletion Source/SwiftLintFramework/Models/RuleDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ public struct RuleDescription: Equatable {
public let nonTriggeringExamples: [String]
public let triggeringExamples: [String]
public let corrections: [String: String]
public let needsSourceKit: Bool

public var consoleDescription: String { return "\(name) (\(identifier)): \(description)" }

public init(identifier: String, name: String, description: String,
nonTriggeringExamples: [String] = [], triggeringExamples: [String] = [],
corrections: [String: String] = [:]) {
corrections: [String: String] = [:], needsSourceKit: Bool = true) {
self.identifier = identifier
self.name = name
self.description = description
self.nonTriggeringExamples = nonTriggeringExamples
self.triggeringExamples = triggeringExamples
self.corrections = corrections
self.needsSourceKit = needsSourceKit
}
}

Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Rules/FileLengthRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public struct FileLengthRule: ConfigurationProviderRule {
],
triggeringExamples: [
Repeat(count: 401, repeatedValue: "//\n").joinWithSeparator("")
]
],
needsSourceKit: false
)

public func validateFile(file: File) -> [StyleViolation] {
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Rules/LeadingWhitespaceRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public struct LeadingWhitespaceRule: ConfigurationProviderRule {
name: "Leading Whitespace",
description: "Files should not contain leading whitespace.",
nonTriggeringExamples: [ "//\n" ],
triggeringExamples: [ "\n", " //\n" ]
triggeringExamples: [ "\n", " //\n" ],
needsSourceKit: false
)

public func validateFile(file: File) -> [StyleViolation] {
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Rules/LineLengthRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public struct LineLengthRule: ConfigurationProviderRule {
],
triggeringExamples: [
Repeat(count: 101, repeatedValue: "/").joinWithSeparator("") + "\n"
]
],
needsSourceKit: false
)

public func validateFile(file: File) -> [StyleViolation] {
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Rules/TrailingNewlineRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public struct TrailingNewlineRule: CorrectableRule, ConfigurationProviderRule {
"let a = 0": "let a = 0\n",
"let b = 0\n\n": "let b = 0\n",
"let c = 0\n\n\n\n": "let c = 0\n"
]
],
needsSourceKit: false
)

public func validateFile(file: File) -> [StyleViolation] {
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Rules/TrailingWhitespaceRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public struct TrailingWhitespaceRule: CorrectableRule, ConfigurationProviderRule
description: "Lines should not have trailing whitespace.",
nonTriggeringExamples: [ "//\n" ],
triggeringExamples: [ "// \n" ],
corrections: [ "// \n": "//\n" ]
corrections: [ "// \n": "//\n" ],
needsSourceKit: false
)

public func validateFile(file: File) -> [StyleViolation] {
Expand Down
21 changes: 13 additions & 8 deletions Source/swiftlint/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@
// Copyright (c) 2015 Realm. All rights reserved.
//

import Foundation
import Commandant
import SwiftLintFramework

let registry = CommandRegistry<CommandantError<()>>()
registry.register(LintCommand())
registry.register(AutoCorrectCommand())
registry.register(VersionCommand())
registry.register(RulesCommand())
registry.register(HelpCommand(registry: registry))
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let registry = CommandRegistry<CommandantError<()>>()
registry.register(LintCommand())
registry.register(AutoCorrectCommand())
registry.register(VersionCommand())
registry.register(RulesCommand())
registry.register(HelpCommand(registry: registry))

registry.main(defaultVerb: LintCommand().verb) { error in
queuedPrintError(String(error))
registry.main(defaultVerb: LintCommand().verb) { error in
queuedPrintError(String(error))
}
}

dispatch_main()
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
69F88BF71BDA38A6005E7CAE /* OpeningBraceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692B1EB11BD7E00F00EAABFF /* OpeningBraceRule.swift */; };
6CB514E91C760C6900FA02C4 /* Structure+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */; };
6CC4259B1C77046200AEA885 /* SyntaxMap+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */; };
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */; };
83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; };
83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleDescription.swift */; };
B58AEED61C492C7B00E901FD /* ForceUnwrappingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */; };
Expand Down Expand Up @@ -200,6 +201,7 @@
695BE9CE1BDFD92B0071E985 /* CommaRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommaRule.swift; sourceTree = "<group>"; };
6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Structure+SwiftLint.swift"; sourceTree = "<group>"; };
6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyntaxMap+SwiftLint.swift"; sourceTree = "<group>"; };
6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceKitCrashTests.swift; sourceTree = "<group>"; };
83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = "<group>"; };
83D71E261B131EB5000395DE /* RuleDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDescription.swift; sourceTree = "<group>"; };
B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceUnwrappingRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -501,6 +503,7 @@
3B30C4A01C3785B300E04027 /* YamlParserTests.swift */,
3BCC04D31C502BAB006073C3 /* RuleConfigurationTests.swift */,
3BB47D861C51DE6E00AE6A10 /* CustomRulesTests.swift */,
6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */,
);
name = SwiftLintFrameworkTests;
path = Tests/SwiftLintFramework;
Expand Down Expand Up @@ -900,6 +903,7 @@
E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */,
02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */,
D4348EEA1C46122C007707FB /* FunctionBodyLengthRuleTests.swift in Sources */,
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */,
3BB47D871C51DE6E00AE6A10 /* CustomRulesTests.swift in Sources */,
E812249A1B04F85B001783D2 /* TestHelpers.swift in Sources */,
E86396C71BADAFE6002C9E88 /* ReporterTests.swift in Sources */,
Expand Down
Loading

0 comments on commit 62c7d4a

Please sign in to comment.