From 05ac3c9d75a144022d7045647565f05fca0eb595 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 26 Jul 2022 03:55:36 -0400 Subject: [PATCH] Require macOS 12 & Swift 5.6 (#4037) This will unblock using Swift Concurrency features and updating to the latest versions of Swift Argument Parser. This won't drop support for linting projects with an older toolchain / Xcode selected, as long as SwiftLint was _built_ with 5.6+ and is _running_ on macOS 12+. So the main breaking change for end users here is requiring macOS 12 to run. However, the upside to using Swift Concurrency features is worth the breaking change in my opinion. Also being able to stay on recent Swift Argument Parser releases. --- CHANGELOG.md | 8 +++ Package.resolved | 4 +- Package.swift | 18 ++----- README.md | 2 +- .../Extensions/Configuration+Cache.swift | 2 +- .../Extensions/String+md5.swift | 15 ------ .../Extensions/String+sha256.swift | 14 ++++++ .../SwiftLintFramework/Models/Command.swift | 50 +++++-------------- .../Reporters/CodeClimateReporter.swift | 2 +- .../Idiomatic/UnavailableConditionRule.swift | 10 +--- .../Helpers/CompilerArgumentsExtractor.swift | 23 --------- .../Helpers/LintOrAnalyzeArguments.swift | 2 +- .../ReporterTests.swift | 8 +-- .../CannedCodeClimateReporterOutput.json | 8 +-- azure-pipelines.yml | 6 --- 15 files changed, 56 insertions(+), 116 deletions(-) delete mode 100644 Source/SwiftLintFramework/Extensions/String+md5.swift create mode 100644 Source/SwiftLintFramework/Extensions/String+sha256.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 0353b4e1a..04a11add3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,14 @@ macOS < 12. positional paths that can be added last to both commands. [SimplyDanny](https://github.com/SimplyDanny) +* SwiftLint now requires Swift 5.6 or higher to build, and macOS 12 + or higher to run. + [JP Simard](https://github.com/jpsim) + +* Code Climate reports now use SHA256 strings as the issue fingerprint + values. + [JP Simard](https://github.com/jpsim) + #### Experimental * None. diff --git a/Package.resolved b/Package.resolved index ff3752f7f..b38b75fb3 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/apple/swift-argument-parser.git", "state": { "branch": null, - "revision": "e394bf350e38cb100b6bc4172834770ede1b7232", - "version": "1.0.3" + "revision": "df9ee6676cd5b3bf5b330ec7568a5644f547201b", + "version": "1.1.3" } }, { diff --git a/Package.swift b/Package.swift index 97bdc8344..dc980f30c 100644 --- a/Package.swift +++ b/Package.swift @@ -9,36 +9,29 @@ private let addCryptoSwift = true private let staticSwiftSyntax = false #endif -#if os(Linux) && compiler(<5.6) -private let swiftSyntaxFiveDotSix = false -#else -private let swiftSyntaxFiveDotSix = true -#endif - let frameworkDependencies: [Target.Dependency] = [ .product(name: "SourceKittenFramework", package: "SourceKitten"), .product(name: "SwiftSyntax", package: "SwiftSyntax"), + .product(name: "SwiftSyntaxParser", package: "SwiftSyntax"), "Yams", ] + (addCryptoSwift ? ["CryptoSwift"] : []) + (staticSwiftSyntax ? ["lib_InternalSwiftSyntaxParser"] : []) -+ (swiftSyntaxFiveDotSix ? [.product(name: "SwiftSyntaxParser", package: "SwiftSyntax")] : []) let package = Package( name: "SwiftLint", - platforms: [.macOS(.v10_12)], + platforms: [.macOS(.v12)], products: [ .executable(name: "swiftlint", targets: ["swiftlint"]), .library(name: "SwiftLintFramework", targets: ["SwiftLintFramework"]) ], dependencies: [ - .package(name: "swift-argument-parser", url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.0.3")), - .package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", - .exact(swiftSyntaxFiveDotSix ? "0.50600.1" : "0.50500.0")), + .package(name: "swift-argument-parser", url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.1.3")), + .package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", .exact("0.50600.1")), .package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.32.0"), .package(url: "https://github.com/jpsim/Yams.git", from: "4.0.2"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), - ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.4.3"))] : []), + ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.5.1"))] : []), targets: [ .executableTarget( name: "swiftlint", @@ -51,7 +44,6 @@ let package = Package( .target( name: "SwiftLintFramework", dependencies: frameworkDependencies, - swiftSettings: swiftSyntaxFiveDotSix ? [.define("SWIFT_SYNTAX_FIVE_DOT_SIX")] : [], // Pass `-dead_strip_dylibs` to ignore the dynamic version of `lib_InternalSwiftSyntaxParser` // that ships with SwiftSyntax because we want the static version from // `StaticInternalSwiftSyntaxParser`. diff --git a/README.md b/README.md index 0fb44f87e..0f59f0eac 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ running it. ### Installing from source: You can also build and install from source by cloning this project and running -`make install` (Xcode 13 or later). +`make install` (Xcode 13.3 or later). ## Usage diff --git a/Source/SwiftLintFramework/Extensions/Configuration+Cache.swift b/Source/SwiftLintFramework/Extensions/Configuration+Cache.swift index 2e6a964f5..5acd514d1 100644 --- a/Source/SwiftLintFramework/Extensions/Configuration+Cache.swift +++ b/Source/SwiftLintFramework/Extensions/Configuration+Cache.swift @@ -65,7 +65,7 @@ extension Configuration { let jsonObject: [Any] = [rootDirectory, cacheRulesDescriptions] if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject), let jsonString = String(data: jsonData, encoding: .utf8) { - return jsonString.md5() + return jsonString.sha256() } queuedFatalError("Could not serialize configuration for cache") } diff --git a/Source/SwiftLintFramework/Extensions/String+md5.swift b/Source/SwiftLintFramework/Extensions/String+md5.swift deleted file mode 100644 index f120d348a..000000000 --- a/Source/SwiftLintFramework/Extensions/String+md5.swift +++ /dev/null @@ -1,15 +0,0 @@ -#if canImport(CommonCrypto) -import CommonCrypto - -extension String { - internal func md5() -> String { - let context = UnsafeMutablePointer.allocate(capacity: 1) - var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) - CC_MD5_Init(context) - CC_MD5_Update(context, self, CC_LONG(lengthOfBytes(using: .utf8))) - CC_MD5_Final(&digest, context) - context.deallocate() - return digest.reduce(into: "") { $0.append(String(format: "%02x", $1)) } - } -} -#endif diff --git a/Source/SwiftLintFramework/Extensions/String+sha256.swift b/Source/SwiftLintFramework/Extensions/String+sha256.swift new file mode 100644 index 000000000..f9e1d675e --- /dev/null +++ b/Source/SwiftLintFramework/Extensions/String+sha256.swift @@ -0,0 +1,14 @@ +#if canImport(CommonCrypto) +import CommonCrypto + +extension String { + internal func sha256() -> String { + let theData = data(using: .utf8)! + return theData.withUnsafeBytes { bytes in + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + _ = CC_SHA256(bytes.baseAddress, CC_LONG(theData.count), &hash) + return hash.reduce(into: "") { $0.append(String(format: "%02x", $1)) } + } + } +} +#endif diff --git a/Source/SwiftLintFramework/Models/Command.swift b/Source/SwiftLintFramework/Models/Command.swift index b76325e32..c87ed98bf 100644 --- a/Source/SwiftLintFramework/Models/Command.swift +++ b/Source/SwiftLintFramework/Models/Command.swift @@ -1,33 +1,4 @@ import Foundation -import SourceKittenFramework - -#if os(Linux) -private extension Scanner { - func scanString(string: String) -> String? { - return scanString(string) - } -} -#else -private extension Scanner { - func scanUpToString(_ string: String) -> String? { - var result: NSString? - let success = scanUpTo(string, into: &result) - if success { - return result?.bridge() - } - return nil - } - - func scanString(string: String) -> String? { - var result: NSString? - let success = scanString(string, into: &result) - if success { - return result?.bridge() - } - return nil - } -} -#endif /// A SwiftLint-interpretable command to modify SwiftLint's behavior embedded as comments in source code. public struct Command: Equatable { @@ -100,7 +71,7 @@ public struct Command: Equatable { /// defined. public init?(actionString: String, line: Int, character: Int) { let scanner = Scanner(string: actionString) - _ = scanner.scanString(string: "swiftlint:") + _ = scanner.scanString("swiftlint:") // (enable|disable)(:previous|:this|:next) guard let actionAndModifierString = scanner.scanUpToString(" ") else { return nil @@ -120,9 +91,12 @@ public struct Command: Equatable { trailingComment = nil } else { // Store any text after the comment delimiter as the trailingComment. - // The addition to scanLocation is to move past the delimiter - let startOfCommentPastDelimiter = scanner.scanLocation + Self.commentDelimiter.count - trailingComment = scanner.string.bridge().substring(from: startOfCommentPastDelimiter) + // The addition to currentIndex is to move past the delimiter + trailingComment = String( + scanner + .string[scanner.currentIndex...] + .dropFirst(Self.commentDelimiter.count) + ) } let ruleTexts = rawRuleTexts.components(separatedBy: .whitespacesAndNewlines).filter { let component = $0.trimmingCharacters(in: .whitespaces) @@ -132,11 +106,13 @@ public struct Command: Equatable { ruleIdentifiers = Set(ruleTexts.map(RuleIdentifier.init(_:))) // Modifier - let hasModifier = actionAndModifierScanner.scanString(string: ":") != nil + let hasModifier = actionAndModifierScanner.scanString(":") != nil if hasModifier { - let modifierString = actionAndModifierScanner.string.bridge() - .substring(from: actionAndModifierScanner.scanLocation) - modifier = Modifier(rawValue: modifierString) + modifier = Modifier( + rawValue: String( + actionAndModifierScanner.string[actionAndModifierScanner.currentIndex...] + ) + ) } else { modifier = nil } diff --git a/Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift b/Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift index e110b6f7b..0b79d78d9 100644 --- a/Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift +++ b/Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift @@ -50,6 +50,6 @@ public struct CodeClimateReporter: Reporter { return [ "\(fingerprintLocation)", "\(violation.ruleIdentifier)" - ].joined().md5() + ].joined().sha256() } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableConditionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableConditionRule.swift index 5acb4ef91..14b6056f1 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableConditionRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableConditionRule.swift @@ -90,10 +90,8 @@ public struct UnavailableConditionRule: ConfigurationProviderRule, AutomaticTest switch check { case is AvailabilityConditionSyntax: return "Use #unavailable instead of #available with an empty body." - #if SWIFT_SYNTAX_FIVE_DOT_SIX case is UnavailabilityConditionSyntax: return "Use #available instead of #unavailable with an empty body." - #endif default: queuedFatalError("Unknown availability check type.") } @@ -123,12 +121,8 @@ private final class UnavailableConditionRuleVisitor: SyntaxVisitor { } private func asAvailabilityCondition(_ condition: Syntax) -> SyntaxProtocol? { - let availability = condition.as(AvailabilityConditionSyntax.self) - #if SWIFT_SYNTAX_FIVE_DOT_SIX - return availability ?? condition.as(UnavailabilityConditionSyntax.self) - #else - return availability - #endif + condition.as(AvailabilityConditionSyntax.self) ?? + condition.as(UnavailabilityConditionSyntax.self) } private func otherAvailabilityCheckInvolved(ifStmt: IfStmtSyntax) -> Bool { diff --git a/Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift b/Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift index c1266cfeb..dac90f2f5 100644 --- a/Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift +++ b/Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift @@ -1,5 +1,4 @@ import Foundation -import SourceKittenFramework struct CompilerArgumentsExtractor { static func allCompilerInvocations(compilerLogs: String) -> [[String]] { @@ -18,28 +17,6 @@ struct CompilerArgumentsExtractor { // MARK: - Private -#if !os(Linux) -private extension Scanner { - func scanUpToString(_ string: String) -> String? { - var result: NSString? - let success = scanUpTo(string, into: &result) - if success { - return result?.bridge() - } - return nil - } - - func scanString(_ string: String) -> String? { - var result: NSString? - let success = scanString(string, into: &result) - if success { - return result?.bridge() - } - return nil - } -} -#endif - private func parseCLIArguments(_ string: String) -> [String] { let escapedSpacePlaceholder = "\u{0}" let scanner = Scanner(string: string) diff --git a/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift b/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift index 8cd946067..c1055788c 100644 --- a/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift +++ b/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift @@ -47,7 +47,7 @@ struct LintOrAnalyzeArguments: ParsableArguments { // `LintOrAnalyzeArguments`. func pathOptionDescription(for mode: LintOrAnalyzeMode) -> ArgumentHelp { - ArgumentHelp(shouldDisplay: false) + ArgumentHelp(visibility: .hidden) } func pathsArgumentDescription(for mode: LintOrAnalyzeMode) -> ArgumentHelp { diff --git a/Tests/SwiftLintFrameworkTests/ReporterTests.swift b/Tests/SwiftLintFrameworkTests/ReporterTests.swift index 5e87fdd00..028f6805e 100644 --- a/Tests/SwiftLintFrameworkTests/ReporterTests.swift +++ b/Tests/SwiftLintFrameworkTests/ReporterTests.swift @@ -42,7 +42,7 @@ class ReporterTests: XCTestCase { severity: .error, location: location, reason: "Shorthand syntactic sugar should be used" + - ", i.e. [Int] instead of Array."), + ", i.e. [Int] instead of Array."), StyleViolation(ruleDescription: ColonRule.description, severity: .error, location: Location(file: nil), @@ -118,9 +118,9 @@ class ReporterTests: XCTestCase { func testHTMLReporter() { let expectedOutput = stringFromFile("CannedHTMLReporterOutput.html") let result = HTMLReporter.generateReport( - generateViolations(), - swiftlintVersion: "1.2.3", - dateString: "13/12/2016" + generateViolations(), + swiftlintVersion: "1.2.3", + dateString: "13/12/2016" ) XCTAssertEqual(result, expectedOutput) } diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json b/Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json index 64f31d6e8..c8d56f3b5 100644 --- a/Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json +++ b/Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json @@ -3,7 +3,7 @@ "check_name" : "Line Length", "description" : "Violation Reason.", "engine_name" : "SwiftLint", - "fingerprint" : "091a80392f7aebde05dab908162c3629", + "fingerprint" : "917a85854a9500cfd28520fa4875a3ecbba171e522f13452c7adec71fc14497a", "location" : { "lines" : { "begin" : 1, @@ -18,7 +18,7 @@ "check_name" : "Line Length", "description" : "Violation Reason.", "engine_name" : "SwiftLint", - "fingerprint" : "091a80392f7aebde05dab908162c3629", + "fingerprint" : "917a85854a9500cfd28520fa4875a3ecbba171e522f13452c7adec71fc14497a", "location" : { "lines" : { "begin" : 1, @@ -33,7 +33,7 @@ "check_name" : "Syntactic Sugar", "description" : "Shorthand syntactic sugar should be used, i.e. [Int] instead of Array.", "engine_name" : "SwiftLint", - "fingerprint" : "624a4e77e797c9e6a42c82b6428e9c45", + "fingerprint" : "6477f518963149b01807a31e7067be97dc39a30b93673e1e246f812e9ea5ef21", "location" : { "lines" : { "begin" : 1, @@ -48,7 +48,7 @@ "check_name" : "Colon Spacing", "description" : "Colons should be next to the identifier when specifying a type and next to the key in dictionary literals.", "engine_name" : "SwiftLint", - "fingerprint" : "1f9389145aa6d7e89a582fce5ae54659", + "fingerprint" : "9b1ddedc847d23a54124cb02a56452fc01c23df8f3babc07a6a68cf2449b14a6", "location" : { "lines" : { "begin" : null, diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c65c856bd..b0032a383 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,8 +8,6 @@ jobs: strategy: maxParallel: 10 matrix: - swift55: - containerImage: swift:5.5 swift56: containerImage: swift:5.6 container: $[ variables['containerImage'] ] @@ -23,8 +21,6 @@ jobs: strategy: maxParallel: 10 matrix: - xcode1321: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app xcode1331: DEVELOPER_DIR: /Applications/Xcode_13.3.1.app steps: @@ -41,8 +37,6 @@ jobs: strategy: maxParallel: 10 matrix: - xcode1321: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app xcode1331: DEVELOPER_DIR: /Applications/Xcode_13.3.1.app steps: