diff --git a/.gitmodules b/.gitmodules index d451957f2..4979a82c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "Carthage/Checkouts/Yams"] path = Carthage/Checkouts/Yams url = https://github.com/jpsim/Yams.git +[submodule "SwiftSyntax"] + path = SwiftSyntax + url = https://github.com/apple/swift-syntax.git diff --git a/.swiftlint.yml b/.swiftlint.yml index 78e0b69dd..10367d7b7 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -54,6 +54,7 @@ opt_in_rules: - reduce_into - redundant_nil_coalescing - redundant_type_annotation + - return_value_from_void_function - single_test_class - sorted_first_last - sorted_imports diff --git a/CHANGELOG.md b/CHANGELOG.md index f9089dd7e..bc5115eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ [Paul Taykalo](https://github.com/PaulTaykalo) [JP Simard](https://github.com/jpsim) +* SwiftLint now imports [SwiftSyntax](https://github.com/apple/swift-syntax) + and requires Xcode 11.0 to build. + [Marcelo Fabri](https://github.com/marcelofabri) + #### Experimental * None. @@ -26,6 +30,14 @@ [ZevEisenberg](https://github.com/ZevEisenberg) [#3040](https://github.com/realm/SwiftLint/pull/3040) +* Introduce a new `SyntaxRule` that enables writing rules using + [SwiftSyntax](https://github.com/apple/swift-syntax). + [Marcelo Fabri](https://github.com/marcelofabri) + +* Add `return_value_from_void_function` opt-in rule to warn against using + `return ` in a function that is `Void`. + [Marcelo Fabri](https://github.com/marcelofabri) + #### Bug Fixes * Fix `discarded_notification_center_observer` false positives when diff --git a/Package.resolved b/Package.resolved index ad29737ff..8f440c17e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -55,6 +55,15 @@ "version": "0.29.0" } }, + { + "package": "SwiftSyntax", + "repositoryURL": "https://github.com/apple/swift-syntax.git", + "state": { + "branch": null, + "revision": "3e3eb191fcdbecc6031522660c4ed6ce25282c25", + "version": "0.50100.0" + } + }, { "package": "SwiftyTextTable", "repositoryURL": "https://github.com/scottrhoyt/SwiftyTextTable.git", diff --git a/Package.swift b/Package.swift index 99a64d209..dfe1a024b 100644 --- a/Package.swift +++ b/Package.swift @@ -7,8 +7,17 @@ private let addCryptoSwift = false private let addCryptoSwift = true #endif +#if compiler(>=5.1.0) +private let addSwiftSyntax = true +#else +private let addSwiftSyntax = false +#endif + let package = Package( name: "SwiftLint", + platforms: [ + .macOS(.v10_12) + ], products: [ .executable(name: "swiftlint", targets: ["swiftlint"]), .library(name: "SwiftLintFramework", targets: ["SwiftLintFramework"]) @@ -18,7 +27,8 @@ let package = Package( .package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.29.0")), .package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), - ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []), + ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []) + + (addSwiftSyntax ? [.package(url: "https://github.com/apple/swift-syntax.git", .exact("0.50100.0"))] : []), targets: [ .target( name: "swiftlint", @@ -33,7 +43,8 @@ let package = Package( dependencies: [ "SourceKittenFramework", "Yams", - ] + (addCryptoSwift ? ["CryptoSwift"] : []) + ] + (addCryptoSwift ? ["CryptoSwift"] : []) + + (addSwiftSyntax ? ["SwiftSyntax"] : []) ), .testTarget( name: "SwiftLintFrameworkTests", diff --git a/README.md b/README.md index 7494e7748..b65ac5b6f 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ running it. ### Compiling from source: You can also build from source by cloning this project and running -`git submodule update --init --recursive; make install` (Xcode 10.2 or later). +`script/bootstrap; make install` (Xcode 11.0 or later). ### Known Installation Issues On MacOS Before 10.14.4 diff --git a/Source/SwiftLintFramework/Extensions/SwiftLintFile+Cache.swift b/Source/SwiftLintFramework/Extensions/SwiftLintFile+Cache.swift index 36b7d609e..52849cb9a 100644 --- a/Source/SwiftLintFramework/Extensions/SwiftLintFile+Cache.swift +++ b/Source/SwiftLintFramework/Extensions/SwiftLintFile+Cache.swift @@ -1,5 +1,8 @@ import Foundation import SourceKittenFramework +#if canImport(SwiftSyntax) +import SwiftSyntax +#endif private typealias FileCacheKey = Int private var responseCache = Cache({ file -> [String: SourceKitRepresentable]? in @@ -30,6 +33,20 @@ private var syntaxMapCache = Cache({ file in private var syntaxKindsByLinesCache = Cache({ file in file.syntaxKindsByLine() }) private var syntaxTokensByLinesCache = Cache({ file in file.syntaxTokensByLine() }) +#if canImport(SwiftSyntax) +private var syntaxCache = Cache({ file -> SourceFileSyntax? in + do { + if let path = file.path { + return try SyntaxParser.parse(URL(fileURLWithPath: path)) + } + + return try SyntaxParser.parse(source: file.contents) + } catch { + return nil + } +}) +#endif + internal typealias AssertHandler = () -> Void private var assertHandlers = [FileCacheKey: AssertHandler]() @@ -158,6 +175,19 @@ extension SwiftLintFile { return syntaxMap } + #if canImport(SwiftSyntax) + internal var syntax: SourceFileSyntax { + guard let syntax = syntaxCache.get(self) else { + if let handler = assertHandler { + handler() + return SourceFileSyntax({ _ in }) + } + queuedFatalError("Never call this for file that sourcekitd fails.") + } + return syntax + } + #endif + internal var syntaxTokensByLines: [[SwiftLintSyntaxToken]] { guard let syntaxTokensByLines = syntaxTokensByLinesCache.get(self) else { if let handler = assertHandler { @@ -189,6 +219,9 @@ extension SwiftLintFile { syntaxMapCache.invalidate(self) syntaxTokensByLinesCache.invalidate(self) syntaxKindsByLinesCache.invalidate(self) + #if canImport(SwiftSyntax) + syntaxCache.invalidate(self) + #endif } internal static func clearCaches() { @@ -200,5 +233,8 @@ extension SwiftLintFile { syntaxMapCache.clear() syntaxTokensByLinesCache.clear() syntaxKindsByLinesCache.clear() + #if canImport(SwiftSyntax) + syntaxCache.clear() + #endif } } diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index 8c8aca574..670c10671 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -147,6 +147,7 @@ public let masterRuleList = RuleList(rules: [ RequiredDeinitRule.self, RequiredEnumCaseRule.self, ReturnArrowWhitespaceRule.self, + ReturnValueFromVoidFunctionRule.self, ShorthandOperatorRule.self, SingleTestClassRule.self, SortedFirstLastRule.self, diff --git a/Source/SwiftLintFramework/Protocols/SyntaxRule.swift b/Source/SwiftLintFramework/Protocols/SyntaxRule.swift new file mode 100644 index 000000000..a83dff5ed --- /dev/null +++ b/Source/SwiftLintFramework/Protocols/SyntaxRule.swift @@ -0,0 +1,44 @@ +import Foundation +#if canImport(SwiftSyntax) +import SwiftSyntax +#endif + +/// A rule that leverages the SwiftSyntax library. +public protocol SyntaxRule: Rule {} + +#if canImport(SwiftSyntax) +/// A SwiftSyntax visitor that collects data to provide violations for a specific rule. +public protocol SyntaxRuleVisitor: SyntaxVisitor { + /// The rule that uses this visitor. + associatedtype Rule: SyntaxRule + + /// Returns the violations that should be calculated based on data that was accumulated during the `visit` methods. + func violations(for rule: Rule, in file: SwiftLintFile) -> [StyleViolation] +} + +public extension SyntaxRule { + /// Wraps computation of violations based on a visitor. + func validate(file: SwiftLintFile, + visitor: Visitor) -> [StyleViolation] where Visitor.Rule == Self { + let lock = NSLock() + var visitor = visitor + + // https://bugs.swift.org/browse/SR-11170 + let work = DispatchWorkItem { + lock.lock() + file.syntax.walk(&visitor) + lock.unlock() + } + let thread = Thread { + work.perform() + } + thread.stackSize = 8 << 20 // 8 MB. + thread.start() + work.wait() + + lock.lock() + defer { lock.unlock() } + return visitor.violations(for: self, in: file) + } +} +#endif diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift new file mode 100644 index 000000000..518b124d7 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift @@ -0,0 +1,76 @@ +import SourceKittenFramework +#if canImport(SwiftSyntax) +import SwiftSyntax +#endif + +public struct ReturnValueFromVoidFunctionRule: ConfigurationProviderRule, SyntaxRule, OptInRule, + AutomaticTestableRule { + public var configuration = SeverityConfiguration(.warning) + + public init() {} + + public static let description = RuleDescription( + identifier: "return_value_from_void_function", + name: "Return Value from Void Function", + description: "Returning values from Void functions should be avoided.", + kind: .idiomatic, + minSwiftVersion: .fiveDotOne, + nonTriggeringExamples: ReturnValueFromVoidFunctionRuleExamples.nonTriggeringExamples, + triggeringExamples: ReturnValueFromVoidFunctionRuleExamples.triggeringExamples + ) + + public func validate(file: SwiftLintFile) -> [StyleViolation] { + #if canImport(SwiftSyntax) + return validate(file: file, visitor: ReturnVisitor()) + #else + return [] + #endif + } +} + +#if canImport(SwiftSyntax) +private class ReturnVisitor: SyntaxRuleVisitor { + private var positions = [AbsolutePosition]() + + func visit(_ node: ReturnStmtSyntax) -> SyntaxVisitorContinueKind { + if node.expression != nil, + let functionNode = node.enclosingFunction(), + functionNode.returnsVoid { + positions.append(node.positionAfterSkippingLeadingTrivia) + } + return .visitChildren + } + + func violations(for rule: ReturnValueFromVoidFunctionRule, in file: SwiftLintFile) -> [StyleViolation] { + return positions.map { position in + StyleViolation(ruleDescription: type(of: rule).description, + severity: rule.configuration.severity, + location: Location(file: file, byteOffset: ByteCount(position.utf8Offset))) + } + } +} + +private extension Syntax { + func enclosingFunction() -> FunctionDeclSyntax? { + if let node = self as? FunctionDeclSyntax { + return node + } + + if self is ClosureExprSyntax || self is VariableDeclSyntax { + return nil + } + + return parent?.enclosingFunction() + } +} + +private extension FunctionDeclSyntax { + var returnsVoid: Bool { + if let type = signature.output?.returnType as? SimpleTypeIdentifierSyntax { + return type.name.text == "Void" + } + + return signature.output?.returnType == nil + } +} +#endif diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRuleExamples.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRuleExamples.swift new file mode 100644 index 000000000..d3adb651e --- /dev/null +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ReturnValueFromVoidFunctionRuleExamples.swift @@ -0,0 +1,234 @@ +// swiftlint:disable:next type_body_length +internal struct ReturnValueFromVoidFunctionRuleExamples { + static let nonTriggeringExamples = [ + Example(""" + func foo() { + return + } + """), + Example(""" + func foo() { + return /* a comment */ + } + """), + Example(""" + func foo() -> Int { + return 1 + } + """), + Example(""" + func foo() -> Void { + if condition { + return + } + bar() + } + """), + Example(""" + func foo() { + return; + bar() + } + """), + Example("func test() {}"), + Example(""" + init?() { + guard condition else { + return nil + } + } + """), + Example(""" + init?(arg: String?) { + guard arg != nil else { + return nil + } + } + """), + Example(""" + func test() { + guard condition else { + return + } + } + """), + Example(""" + func test() -> Result { + func other() {} + func otherVoid() -> Void {} + } + """), + Example(""" + func test() -> Int? { + return nil + } + """), + Example(""" + func test() { + if bar { + print("") + return + } + let foo = [1, 2, 3].filter { return true } + return + } + """), + Example(""" + func test() { + guard foo else { + bar() + return + } + } + """), + Example(""" + func spec() { + var foo: Int { + return 0 + } + """) + ] + + static let triggeringExamples = [ + Example(""" + func foo() { + ↓return bar() + } + """), + Example(""" + func foo() { + ↓return self.bar() + } + """), + Example(""" + func foo() -> Void { + ↓return bar() + } + """), + Example(""" + func foo() -> Void { + ↓return /* comment */ bar() + } + """), + Example(""" + func foo() { + ↓return + self.bar() + } + """), + Example(""" + func foo() { + variable += 1 + ↓return + variable += 1 + } + """), + Example(""" + func initThing() { + guard foo else { + ↓return print("") + } + } + """), + Example(""" + // Leading comment + func test() { + guard condition else { + ↓return assertionfailure("") + } + } + """), + Example(""" + func test() -> Result { + func other() { + guard false else { + ↓return assertionfailure("") + } + } + func otherVoid() -> Void {} + } + """), + Example(""" + func test() { + guard conditionIsTrue else { + sideEffects() + return // comment + } + guard otherCondition else { + ↓return assertionfailure("") + } + differentSideEffect() + } + """), + Example(""" + func test() { + guard otherCondition else { + ↓return assertionfailure(""); // comment + } + differentSideEffect() + } + """), + Example(""" + func test() { + if x { + ↓return foo() + } + bar() + } + """), + Example(""" + func test() { + switch x { + case .a: + ↓return foo() // return to skip baz() + case .b: + bar() + } + baz() + } + """), + Example(""" + func test() { + if check { + if otherCheck { + ↓return foo() + } + } + bar() + } + """), + Example(""" + func test() { + ↓return foo() + } + """), + Example(""" + func test() { + ↓return foo({ + return bar() + }) + } + """), + Example(""" + func test() { + guard x else { + ↓return foo() + } + bar() + } + """), + Example(""" + func test() { + let closure: () -> () = { + return assert() + } + if check { + if otherCheck { + return // comments are fine + } + } + ↓return foo() + } + """) + ] +} diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index 6ab5d0571..03b04d812 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -295,6 +295,8 @@ D4470D591EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */; }; D4470D5B1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */; }; D4470D5D1EB8004B008A1B2E /* VerticalParameterAlignmentOnCallRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D5C1EB8004B008A1B2E /* VerticalParameterAlignmentOnCallRule.swift */; }; + D4492B9323DD499A004EC27B /* _CSwiftSyntax.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4492B9123DD499A004EC27B /* _CSwiftSyntax.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + D4492B9523DD499A004EC27B /* SwiftSyntax.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4492B9223DD499A004EC27B /* SwiftSyntax.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; D44AD2761C0AA5350048F7B0 /* LegacyConstructorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */; }; D450D1D121EC4A6900E60010 /* StrongIBOutletRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D450D1D021EC4A6900E60010 /* StrongIBOutletRule.swift */; }; D450D1DD21F199F700E60010 /* TrailingClosureConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D450D1DA21F1992E00E60010 /* TrailingClosureConfiguration.swift */; }; @@ -366,11 +368,14 @@ D4DE9133207B4750000FFAA8 /* UnavailableFunctionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DE9131207B4731000FFAA8 /* UnavailableFunctionRule.swift */; }; D4E2BA851F6CD77B00E8E184 /* ArrayInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E2BA841F6CD77B00E8E184 /* ArrayInitRule.swift */; }; D4E4FB5223E7A1A300746FCD /* LineEndingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E4FB5123E7A1A300746FCD /* LineEndingTests.swift */; }; + D4E4FB5523E7A6CF00746FCD /* ReturnValueFromVoidFunctionRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E4FB5323E7A6CF00746FCD /* ReturnValueFromVoidFunctionRuleExamples.swift */; }; + D4E4FB5623E7A6CF00746FCD /* ReturnValueFromVoidFunctionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E4FB5423E7A6CF00746FCD /* ReturnValueFromVoidFunctionRule.swift */; }; D4E92D1F2137B4C9002EDD48 /* IdenticalOperandsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E92D1E2137B4C9002EDD48 /* IdenticalOperandsRule.swift */; }; D4EA77C81F817FD200C315FB /* UnneededBreakInSwitchRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EA77C71F817FD200C315FB /* UnneededBreakInSwitchRule.swift */; }; D4EA77CA1F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */; }; D4EAB3A420E9948E0051C09A /* AutomaticRuleTests.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EAB3A320E9948D0051C09A /* AutomaticRuleTests.generated.swift */; }; D4EABD0C22CE6F5B00635667 /* VerticalParameterAlignmentRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EABD0B22CE6F5B00635667 /* VerticalParameterAlignmentRuleExamples.swift */; }; + D4EF8EA223E695EA0038C26E /* SyntaxRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EF8EA123E695EA0038C26E /* SyntaxRule.swift */; }; D4F10614229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F10613229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift */; }; D4F10616229A331200FDE319 /* LegacyMultipleRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F10615229A331200FDE319 /* LegacyMultipleRule.swift */; }; D4F5851520E99A8A0085C6D8 /* TrailingWhitespaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F5851320E99A720085C6D8 /* TrailingWhitespaceTests.swift */; }; @@ -820,6 +825,8 @@ D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyEnumArgumentsRule.swift; sourceTree = ""; }; D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnusedOptionalBindingRuleTests.swift; sourceTree = ""; }; D4470D5C1EB8004B008A1B2E /* VerticalParameterAlignmentOnCallRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalParameterAlignmentOnCallRule.swift; sourceTree = ""; }; + D4492B9123DD499A004EC27B /* _CSwiftSyntax.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = _CSwiftSyntax.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D4492B9223DD499A004EC27B /* SwiftSyntax.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftSyntax.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyConstructorRule.swift; sourceTree = ""; }; D450D1D021EC4A6900E60010 /* StrongIBOutletRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StrongIBOutletRule.swift; sourceTree = ""; }; D450D1DA21F1992E00E60010 /* TrailingClosureConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailingClosureConfiguration.swift; sourceTree = ""; }; @@ -892,11 +899,14 @@ D4DE9131207B4731000FFAA8 /* UnavailableFunctionRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnavailableFunctionRule.swift; sourceTree = ""; }; D4E2BA841F6CD77B00E8E184 /* ArrayInitRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayInitRule.swift; sourceTree = ""; }; D4E4FB5123E7A1A300746FCD /* LineEndingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineEndingTests.swift; sourceTree = ""; }; + D4E4FB5323E7A6CF00746FCD /* ReturnValueFromVoidFunctionRuleExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnValueFromVoidFunctionRuleExamples.swift; sourceTree = ""; }; + D4E4FB5423E7A6CF00746FCD /* ReturnValueFromVoidFunctionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnValueFromVoidFunctionRule.swift; sourceTree = ""; }; D4E92D1E2137B4C9002EDD48 /* IdenticalOperandsRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdenticalOperandsRule.swift; sourceTree = ""; }; D4EA77C71F817FD200C315FB /* UnneededBreakInSwitchRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnneededBreakInSwitchRule.swift; sourceTree = ""; }; D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiteralExpressionEndIdentationRule.swift; sourceTree = ""; }; D4EAB3A320E9948D0051C09A /* AutomaticRuleTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutomaticRuleTests.generated.swift; sourceTree = ""; }; D4EABD0B22CE6F5B00635667 /* VerticalParameterAlignmentRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalParameterAlignmentRuleExamples.swift; sourceTree = ""; }; + D4EF8EA123E695EA0038C26E /* SyntaxRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyntaxRule.swift; sourceTree = ""; }; D4F10613229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoFallthroughOnlyRuleExamples.swift; sourceTree = ""; }; D4F10615229A331200FDE319 /* LegacyMultipleRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyMultipleRule.swift; sourceTree = ""; }; D4F5851320E99A720085C6D8 /* TrailingWhitespaceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailingWhitespaceTests.swift; sourceTree = ""; }; @@ -998,8 +1008,10 @@ buildActionMask = 2147483647; files = ( E876BFBE1B07828500114ED5 /* SourceKittenFramework.framework in Frameworks */, + D4492B9323DD499A004EC27B /* _CSwiftSyntax.framework in Frameworks */, E8C0DFCD1AD349DB007EE3D4 /* SWXMLHash.framework in Frameworks */, 3BBF2F9D1C640A0F006CD775 /* SwiftyTextTable.framework in Frameworks */, + D4492B9523DD499A004EC27B /* SwiftSyntax.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1356,6 +1368,8 @@ D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */, 6208ED4E20C297AC004E78D1 /* RedundantTypeAnnotationRule.swift */, D4B022B11E10B613007E5297 /* RedundantVoidReturnRule.swift */, + D4E4FB5423E7A6CF00746FCD /* ReturnValueFromVoidFunctionRule.swift */, + D4E4FB5323E7A6CF00746FCD /* ReturnValueFromVoidFunctionRuleExamples.swift */, D4D383842145F550000235BD /* StaticOperatorRule.swift */, D42B45D81F0AF5E30086B683 /* StrictFilePrivateRule.swift */, D44254251DB9C12300492EA4 /* SyntacticSugarRule.swift */, @@ -1391,6 +1405,7 @@ D0D1211A19E87861005E4BAA /* swiftlint */, D0D1216E19E87B05005E4BAA /* SwiftLintFramework */, D0D1217B19E87B05005E4BAA /* SwiftLintFrameworkTests */, + D4492B9023DD499A004EC27B /* Frameworks */, ); indentWidth = 4; sourceTree = ""; @@ -1615,6 +1630,15 @@ path = "Supporting Files"; sourceTree = ""; }; + D4492B9023DD499A004EC27B /* Frameworks */ = { + isa = PBXGroup; + children = ( + D4492B9123DD499A004EC27B /* _CSwiftSyntax.framework */, + D4492B9223DD499A004EC27B /* SwiftSyntax.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; E802ECFE1C56A54600A35AE1 /* Helpers */ = { isa = PBXGroup; children = ( @@ -1674,6 +1698,7 @@ isa = PBXGroup; children = ( E88DEA8B1B0999A000A66CB0 /* ASTRule.swift */, + D4EF8EA123E695EA0038C26E /* SyntaxRule.swift */, E80746F51ECB722F00548D31 /* CacheDescriptionProvider.swift */, A73469401FB12149009B57C7 /* CallPairRule.swift */, E86396C11BADAAE5002C9E88 /* Reporter.swift */, @@ -2062,6 +2087,7 @@ 62A3E95D209E084000547A86 /* EmptyXCTestMethodRule.swift in Sources */, D4C4A34C1DEA4FF000E0E04C /* AttributesConfiguration.swift in Sources */, 29FC197A21382C07006D208C /* DuplicateImportsRuleExamples.swift in Sources */, + D4E4FB5623E7A6CF00746FCD /* ReturnValueFromVoidFunctionRule.swift in Sources */, 83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */, D4470D571EB69225008A1B2E /* ImplicitReturnRule.swift in Sources */, 8FE3CCBC22DBF8D000B8EA87 /* UnusedDeclarationConfiguration.swift in Sources */, @@ -2077,6 +2103,7 @@ D4C4A3521DEFBBB700E0E04C /* FileHeaderConfiguration.swift in Sources */, 623675B01F960C5C009BE6F3 /* QuickDiscouragedPendingTestRule.swift in Sources */, 287F8B642230843000BDC504 /* NSLocalizedStringRequireBundleRule.swift in Sources */, + D4EF8EA223E695EA0038C26E /* SyntaxRule.swift in Sources */, D47079AD1DFE2FA700027086 /* EmptyParametersRule.swift in Sources */, E87E4A091BFB9CAE00FCFE46 /* SyntaxKind+SwiftLint.swift in Sources */, 3B0B14541C505D6300BE82F7 /* SeverityConfiguration.swift in Sources */, @@ -2322,6 +2349,7 @@ C946FECB1EAE67EE007DD778 /* LetVarWhitespaceRule.swift in Sources */, E881985D1BEA97EB00333A11 /* TrailingWhitespaceRule.swift in Sources */, E832F10B1B17E2F5003F265F /* FileManager+SwiftLint.swift in Sources */, + D4E4FB5523E7A6CF00746FCD /* ReturnValueFromVoidFunctionRuleExamples.swift in Sources */, E816194C1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift in Sources */, D4DABFD71E2C23B1009617B6 /* NotificationCenterDetachmentRule.swift in Sources */, 3BA79C9B1C4767910057E705 /* NSRange+SwiftLint.swift in Sources */, @@ -2480,7 +2508,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0D1212619E878CC005E4BAA /* Debug.xcconfig */; buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; SWIFT_VERSION = 4.0; }; name = Debug; @@ -2489,7 +2517,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0D1212819E878CC005E4BAA /* Release.xcconfig */; buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; SWIFT_VERSION = 4.0; }; name = Release; @@ -2566,7 +2594,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0D1212719E878CC005E4BAA /* Profile.xcconfig */; buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; SWIFT_VERSION = 4.0; }; name = Profile; @@ -2609,7 +2637,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0D1212919E878CC005E4BAA /* Test.xcconfig */; buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; SWIFT_VERSION = 4.0; }; name = Test; diff --git a/SwiftLint.xcworkspace/contents.xcworkspacedata b/SwiftLint.xcworkspace/contents.xcworkspacedata index 9cc392b01..673bf221f 100644 --- a/SwiftLint.xcworkspace/contents.xcworkspacedata +++ b/SwiftLint.xcworkspace/contents.xcworkspacedata @@ -16,6 +16,9 @@ + + diff --git a/SwiftSyntax b/SwiftSyntax new file mode 160000 index 000000000..3e3eb191f --- /dev/null +++ b/SwiftSyntax @@ -0,0 +1 @@ +Subproject commit 3e3eb191fcdbecc6031522660c4ed6ce25282c25 diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index b27008597..822682e4a 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1260,6 +1260,12 @@ extension ReturnArrowWhitespaceRuleTests { ] } +extension ReturnValueFromVoidFunctionRuleTests { + static var allTests: [(String, (ReturnValueFromVoidFunctionRuleTests) -> () throws -> Void)] = [ + ("testWithDefaultConfiguration", testWithDefaultConfiguration) + ] +} + extension RuleConfigurationTests { static var allTests: [(String, (RuleConfigurationTests) -> () throws -> Void)] = [ ("testNameConfigurationSetsCorrectly", testNameConfigurationSetsCorrectly), @@ -1816,6 +1822,7 @@ XCTMain([ testCase(RequiredDeinitRuleTests.allTests), testCase(RequiredEnumCaseRuleTestCase.allTests), testCase(ReturnArrowWhitespaceRuleTests.allTests), + testCase(ReturnValueFromVoidFunctionRuleTests.allTests), testCase(RuleConfigurationTests.allTests), testCase(RuleTests.allTests), testCase(RulesTests.allTests), diff --git a/Tests/SwiftLintFrameworkTests/AutomaticRuleTests.generated.swift b/Tests/SwiftLintFrameworkTests/AutomaticRuleTests.generated.swift index 4d969237f..4a56fc58f 100644 --- a/Tests/SwiftLintFrameworkTests/AutomaticRuleTests.generated.swift +++ b/Tests/SwiftLintFrameworkTests/AutomaticRuleTests.generated.swift @@ -648,6 +648,12 @@ class ReturnArrowWhitespaceRuleTests: XCTestCase { } } +class ReturnValueFromVoidFunctionRuleTests: XCTestCase { + func testWithDefaultConfiguration() { + verifyRule(ReturnValueFromVoidFunctionRule.description) + } +} + class ShorthandOperatorRuleTests: XCTestCase { func testWithDefaultConfiguration() { verifyRule(ShorthandOperatorRule.description) diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Nested.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+Nested.swift index d769ca79a..71d63cf63 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Nested.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+Nested.swift @@ -88,7 +88,8 @@ extension ConfigurationTests { let mergedConfiguration = projectMockConfig0CustomRules.merge(with: projectMockConfig2CustomRules) guard let mergedCustomRules = mergedConfiguration.rules.first(where: { $0 is CustomRules }) as? CustomRules else { - return XCTFail("Custom rule are expected to be present") + XCTFail("Custom rule are expected to be present") + return } XCTAssertTrue( mergedCustomRules.configuration.customRuleConfigurations.contains(where: { $0.identifier == "no_abc" }) @@ -102,7 +103,8 @@ extension ConfigurationTests { let mergedConfiguration = projectMockConfig0CustomRules.merge(with: projectMockConfig2CustomRulesDisabled) guard let mergedCustomRules = mergedConfiguration.rules.first(where: { $0 is CustomRules }) as? CustomRules else { - return XCTFail("Custom rule are expected to be present") + XCTFail("Custom rule are expected to be present") + return } XCTAssertFalse( mergedCustomRules.configuration.customRuleConfigurations.contains(where: { $0.identifier == "no_abc" }) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b3e719eb8..68ca625d5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,15 +23,11 @@ jobs: strategy: maxParallel: 10 matrix: - xcode102: - DEVELOPER_DIR: /Applications/Xcode_10.2.app - xcode103: - DEVELOPER_DIR: /Applications/Xcode_10.3.app xcode111: DEVELOPER_DIR: /Applications/Xcode_11.1.app steps: - - script: git submodule update --init --recursive - displayName: Update git submodules + - script: Script/bootstrap + displayName: Bootstrap - script: | sw_vers xcodebuild -version diff --git a/script/bootstrap b/script/bootstrap index ebd4c8d75..174ce1120 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -21,6 +21,8 @@ main () echo "*** Updating submodules..." update_submodules fi + + cd "SwiftSyntax" && swift package generate-xcodeproj } bootstrap_submodule ()