From d1923573080cda6336eba6bd199cdf0d63b945a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 8 Aug 2024 20:06:15 +0200 Subject: [PATCH 001/260] Update "Publish to BCR" config --- .bcr/config.yml | 3 --- .bcr/metadata.template.json | 5 +++++ .bcr/presubmit.yml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) delete mode 100644 .bcr/config.yml diff --git a/.bcr/config.yml b/.bcr/config.yml deleted file mode 100644 index 89045ecc3b..0000000000 --- a/.bcr/config.yml +++ /dev/null @@ -1,3 +0,0 @@ -fixedReleaser: - login: jpsim - email: jp@jpsim.com diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json index 907734edc3..84b6013c36 100644 --- a/.bcr/metadata.template.json +++ b/.bcr/metadata.template.json @@ -5,6 +5,11 @@ "email": "jp@jpsim.com", "github": "jpsim", "name": "JP Simard" + }, + { + "email": "danny.moesch@icloud.com", + "github": "SimplyDanny", + "name": "Danny Mösch" } ], "repository": [ diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index e1febf595f..80a746c9ad 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -9,7 +9,7 @@ tasks: platform: ubuntu2004 environment: CC: "clang" - SWIFT_VERSION: "5.8.1" + SWIFT_VERSION: "5.10" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$PATH:$SWIFT_HOME/usr/bin" shell_commands: *shell_commands From cffb33100957b75b701cccaa9f5f77b0ef04bcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 18 Aug 2024 22:53:57 +0200 Subject: [PATCH 002/260] Fix spurious Bazel build errors (#5756) --- .buildkite/pipeline.yml | 2 - .../Rules/Lint/ArrayInitRule.swift | 2 +- .../Rules/Lint/UnusedDeclarationRule.swift | 44 +++++++++---------- tools/add-preconcurrency-imports.sh | 14 ------ 4 files changed, 23 insertions(+), 39 deletions(-) delete mode 100755 tools/add-preconcurrency-imports.sh diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4fcb804d9d..22fb23aafe 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -7,8 +7,6 @@ steps: - bazel test --test_output=errors //Tests/... - label: "Build With Strict Concurrency" commands: - - echo "+++ Add @preconcurrency imports" - - ./tools/add-preconcurrency-imports.sh - echo "+++ Build" - bazel build --define strict_concurrency_builtin_rules=true :swiftlint - echo "--- Clean up" diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift index 088c3a878b..9d36b2c15d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax @SwiftSyntaxRule -struct ArrayInitRule: OptInRule { +struct ArrayInitRule: OptInRule, @unchecked Sendable { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift index 3e8cf1d1c1..d851ab72c0 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift @@ -121,6 +121,20 @@ private extension SwiftLintFile { editorOpen: SourceKittenDictionary, compilerArguments: [String], configuration: UnusedDeclarationConfiguration) -> UnusedDeclarationRule.DeclaredUSR? { + // Skip initializers, deinit, enum cases and subscripts since we can't reliably detect if they're used. + let declarationKindsToSkip: Set = [ + .enumelement, + .extensionProtocol, + .extension, + .extensionEnum, + .extensionClass, + .extensionStruct, + .functionConstructor, + .functionDestructor, + .functionSubscript, + .genericTypeParam, + ] + guard let stringKind = indexEntity.kind, stringKind.starts(with: "source.lang.swift.decl."), !stringKind.contains(".accessor."), @@ -196,6 +210,14 @@ private extension SwiftLintFile { } private func shouldIgnoreEntity(_ indexEntity: SourceKittenDictionary, relatedUSRsToSkip: Set) -> Bool { + let declarationAttributesToSkip: Set = [ + .ibaction, + .main, + .nsApplicationMain, + .override, + .uiApplicationMain, + ] + if indexEntity.shouldSkipIndexEntityToWorkAroundSR11985() || indexEntity.shouldSkipRelated(relatedUSRsToSkip: relatedUSRsToSkip) || indexEntity.enclosedSwiftAttributes.contains(where: declarationAttributesToSkip.contains) || @@ -321,25 +343,3 @@ private extension SourceKittenDictionary { return nil } } - -// Skip initializers, deinit, enum cases and subscripts since we can't reliably detect if they're used. -private let declarationKindsToSkip: Set = [ - .enumelement, - .extensionProtocol, - .extension, - .extensionEnum, - .extensionClass, - .extensionStruct, - .functionConstructor, - .functionDestructor, - .functionSubscript, - .genericTypeParam, -] - -private let declarationAttributesToSkip: Set = [ - .ibaction, - .main, - .nsApplicationMain, - .override, - .uiApplicationMain, -] diff --git a/tools/add-preconcurrency-imports.sh b/tools/add-preconcurrency-imports.sh deleted file mode 100755 index e65de3a03c..0000000000 --- a/tools/add-preconcurrency-imports.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -files=( -"Source/SwiftLintBuiltInRules/Rules/Idiomatic/PrivateOverFilePrivateRule.swift" -"Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift" -"Source/SwiftLintBuiltInRules/Rules/Lint/UnusedClosureParameterRule.swift" -"Source/SwiftLintBuiltInRules/Rules/Style/EmptyEnumArgumentsRule.swift" -"Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift" -"Source/SwiftLintBuiltInRules/Rules/Style/TrailingCommaRule.swift" -) - -for file in "${files[@]}"; do - sed -i '' -e 's/import SwiftSyntax$/@preconcurrency import SwiftSyntax/g' "$file" -done From fac959989ea36a4b6063c5485a27642f491cef24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 18 Aug 2024 23:14:17 +0200 Subject: [PATCH 003/260] Add Swift builds running on macOS 13 (#5734) --- azure-pipelines.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 17f5eea128..992185d397 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,6 +15,15 @@ jobs: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test +- job: macOS13 + pool: + vmImage: 'macOS-13' + strategy: + maxParallel: 10 + steps: + - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES + displayName: swift test + # TODO: Re-enable when FB11648454 is fixed # - job: Xcode # pool: @@ -44,7 +53,7 @@ jobs: - script: bundle exec pod lib lint --platforms=macos --verbose displayName: pod lib lint -- job: jazzy +- job: Jazzy pool: vmImage: 'macOS-14' variables: From a0a69a63091aec3e1d41b4a842f18e1ca80d5fff Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Sun, 18 Aug 2024 17:21:34 -0400 Subject: [PATCH 004/260] Update Bazel bzlmod dependencies for `rules_swift` 2.x support (#5736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- MODULE.bazel | 12 ++++++------ Package.resolved | 4 ++-- Package.swift | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 84ed011044..c1055f246d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -8,11 +8,11 @@ module( bazel_dep(name = "apple_support", version = "1.16.0", repo_name = "build_bazel_apple_support") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "platforms", version = "0.0.10") -bazel_dep(name = "rules_apple", version = "3.6.0", repo_name = "build_bazel_rules_apple") -bazel_dep(name = "rules_swift", version = "1.18.0", repo_name = "build_bazel_rules_swift") -bazel_dep(name = "sourcekitten", version = "0.35.0", repo_name = "com_github_jpsim_sourcekitten") -bazel_dep(name = "swift-syntax", version = "600.0.0-prerelease-2024-07-24", repo_name = "SwiftSyntax") -bazel_dep(name = "swift_argument_parser", version = "1.3.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser") +bazel_dep(name = "rules_apple", version = "3.8.0", repo_name = "build_bazel_rules_apple") +bazel_dep(name = "rules_swift", version = "2.1.1", repo_name = "build_bazel_rules_swift") +bazel_dep(name = "sourcekitten", version = "0.36.0", repo_name = "com_github_jpsim_sourcekitten") +bazel_dep(name = "swift-syntax", version = "600.0.0-prerelease-2024-08-14", repo_name = "SwiftSyntax") +bazel_dep(name = "swift_argument_parser", version = "1.3.1.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser") bazel_dep(name = "yams", version = "5.1.3", repo_name = "sourcekitten_com_github_jpsim_yams") swiftlint_repos = use_extension("//bazel:repos.bzl", "swiftlint_repos_bzlmod") @@ -31,4 +31,4 @@ use_repo(apple_cc_configure, "local_config_apple_cc") # Dev Dependencies -bazel_dep(name = "rules_xcodeproj", version = "1.13.0", dev_dependency = True) +bazel_dep(name = "rules_xcodeproj", version = "2.6.1", dev_dependency = True) diff --git a/Package.resolved b/Package.resolved index 30811f5487..204129e258 100644 --- a/Package.resolved +++ b/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { - "revision" : "82a453c2dfa335c7e778695762438dfe72b328d2", - "version" : "600.0.0-prerelease-2024-07-24" + "revision" : "515f79b522918f83483068d99c68daeb5116342d", + "version" : "600.0.0-prerelease-2024-08-14" } }, { diff --git a/Package.swift b/Package.swift index 9d0c88c9c4..34fec6bfef 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.1"), - .package(url: "https://github.com/swiftlang/swift-syntax.git", exact: "600.0.0-prerelease-2024-07-24"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", exact: "600.0.0-prerelease-2024-08-14"), .package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.35.0")), .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), From 5454bc373cd58ea77f8292d49eb962fc532b9392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Aug 2024 21:41:07 +0000 Subject: [PATCH 005/260] Bump `rexml` from 3.2.8 to 3.3.3 (#5757) Bumps the bundler group with 1 update in the / directory: [rexml](https://github.com/ruby/rexml). Updates `rexml` from 3.2.8 to 3.3.3 - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.2.8...v3.3.3) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect dependency-group: bundler ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6058ee2b83..f09505a870 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,8 +122,8 @@ GEM public_suffix (4.0.7) rchardet (1.8.0) redcarpet (3.6.0) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.3) + strscan rouge (4.3.0) ruby-macho (2.5.1) ruby2_keywords (0.0.5) @@ -144,13 +144,13 @@ GEM unicode-display_width (2.4.2) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.22.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) PLATFORMS arm64-darwin-21 From 21b1a03e2bc1e70c6f3e26ed5b2b851375f98505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 19 Aug 2024 20:56:32 +0200 Subject: [PATCH 006/260] Ignore initializers with attributes in `unneeded_synthesized_initializer` rule (#5758) --- CHANGELOG.md | 4 +++- .../Idiomatic/UnneededSynthesizedInitializerRule.swift | 6 ++---- .../UnneededSynthesizedInitializerRuleExamples.swift | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2750761f45..7d1e4d0c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ #### Bug Fixes -* None. +* Ignore initializers with attributes in `unneeded_synthesized_initializer` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5153](https://github.com/realm/SwiftLint/issues/5153) ## 0.56.1: Heat Pump Dryer diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift index d64e30da22..ddd73209a8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift @@ -112,7 +112,7 @@ private extension StructDeclSyntax { self.initializerParameters($0.parameterList, match: varDecls) && (($0.parameterList.isEmpty && hasNoSideEffects($0.body)) || initializerBody($0.body, matches: varDecls)) && - initializerModifiers($0.modifiers, match: varDecls) && !$0.isInlinable + initializerModifiers($0.modifiers, match: varDecls) && $0.attributes.isEmpty } } @@ -236,9 +236,7 @@ private extension InitializerDeclSyntax { var hasThrowsOrRethrowsKeyword: Bool { signature.effectSpecifiers?.throwsClause?.throwsSpecifier != nil } - var isInlinable: Bool { - attributes.contains(attributeNamed: "inlinable") - } + var parameterList: FunctionParameterListSyntax { signature.parameterClause.parameters } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRuleExamples.swift index 66c19ebee9..ebeec84c4e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRuleExamples.swift @@ -244,6 +244,12 @@ enum UnneededSynthesizedInitializerRuleExamples { init() {} } """, excludeFromDocumentation: true), + Example(""" + struct Foo { + @available(*, unavailable) + init() {} + } + """), ] static let triggering = [ From 314c91f426cfc41f0e5e51b284c03b3aad4fb66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 19 Aug 2024 21:42:10 +0200 Subject: [PATCH 007/260] Check all nested `if` expressions in `contrasted_opening_brace` rule (#5759) --- CHANGELOG.md | 4 ++++ .../Style/ContrastedOpeningBraceRule.swift | 13 +++++++++-- .../ContrastedOpeningBraceRuleExamples.swift | 22 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1e4d0c68..cb15dd41b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5153](https://github.com/realm/SwiftLint/issues/5153) +* Check `if` expressions nested arbitrarily deep in `contrasted_opening_brace` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5752](https://github.com/realm/SwiftLint/issues/5752) + ## 0.56.1: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift index 318d6590b6..b186c2b51f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift @@ -72,8 +72,8 @@ private extension BracedSyntax { if let catchClause = parent?.as(CatchClauseSyntax.self) { return catchClause.parent?.as(CatchClauseListSyntax.self)?.parent?.as(DoStmtSyntax.self) } - if parent?.as(IfExprSyntax.self)?.keyPathInParent == \IfExprSyntax.elseBody { - return parent?.parent + if let ifExpr = parent?.as(IfExprSyntax.self) { + return ifExpr.indentationDecidingParent } if let binding = parent?.as(PatternBindingSyntax.self) { return binding.parent?.as(PatternBindingListSyntax.self)?.parent?.as(VariableDeclSyntax.self) @@ -81,3 +81,12 @@ private extension BracedSyntax { return parent } } + +private extension IfExprSyntax { + var indentationDecidingParent: any SyntaxProtocol { + if keyPathInParent == \IfExprSyntax.elseBody, let parent = parent?.as(IfExprSyntax.self) { + return parent.indentationDecidingParent + } + return self + } +} diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift index bd9238c077..41d915d9e6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift @@ -104,6 +104,18 @@ struct ContrastedOpeningBraceRuleExamples { return } """), + Example(""" + if c1 + { + return + } else if c2 + { + return + } else if c3 + { + return + } + """), ] static let triggeringExamples = [ @@ -193,6 +205,16 @@ struct ContrastedOpeningBraceRuleExamples { // code here } """), + Example(""" + if c1 ↓{ + return + } else if c2↓{ + return + } else if c3 + ↓{ + return + } + """), ] static let corrections = [ From d5f7f9c29a67c093a4fd570d07e91cfb082aecc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 19 Aug 2024 22:19:57 +0200 Subject: [PATCH 008/260] Align left closure brace with associated parent function call (#5760) --- CHANGELOG.md | 4 ++++ .../Style/ContrastedOpeningBraceRule.swift | 12 ++++++++++ .../ContrastedOpeningBraceRuleExamples.swift | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb15dd41b5..92a9b7bc63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5752](https://github.com/realm/SwiftLint/issues/5752) +* Align left closure brace with associated parent function call in `contrasted_opening_brace` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5752](https://github.com/realm/SwiftLint/issues/5752) + ## 0.56.1: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift index b186c2b51f..fdd1a939db 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift @@ -78,6 +78,18 @@ private extension BracedSyntax { if let binding = parent?.as(PatternBindingSyntax.self) { return binding.parent?.as(PatternBindingListSyntax.self)?.parent?.as(VariableDeclSyntax.self) } + if let closure = `as`(ClosureExprSyntax.self), + closure.keyPathInParent == \FunctionCallExprSyntax.trailingClosure { + var indentationDecidingToken = closure.leftBrace + repeat { + if let previousToken = indentationDecidingToken.previousToken(viewMode: .sourceAccurate) { + indentationDecidingToken = previousToken + } else { + break + } + } while indentationDecidingToken.leadingTrivia.containsNewlines() == false + return indentationDecidingToken + } return parent } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift index 41d915d9e6..681bc3dd14 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift @@ -116,6 +116,12 @@ struct ContrastedOpeningBraceRuleExamples { return } """), + Example(""" + let a = f.map + { a in + a + } + """), ] static let triggeringExamples = [ @@ -215,6 +221,13 @@ struct ContrastedOpeningBraceRuleExamples { return } """), + Example(""" + func f() + { + return a.map + ↓{ $0 } + } + """), ] static let corrections = [ @@ -434,5 +447,14 @@ struct ContrastedOpeningBraceRuleExamples { // nothing } """), + Example(""" + a.b { $0 } + .c { $1 } + """): Example(""" + a.b + { $0 } + .c + { $1 } + """), ] } From 74f47780701c4768935a5013b8493f20bcec496b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 19 Aug 2024 23:26:48 +0200 Subject: [PATCH 009/260] Align left brace of additional trailing closures with right brace of previous trailing closure (#5761) --- CHANGELOG.md | 5 ++++ .../Style/ContrastedOpeningBraceRule.swift | 27 ++++++++++++------- .../ContrastedOpeningBraceRuleExamples.swift | 22 +++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a9b7bc63..610cd812e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5752](https://github.com/realm/SwiftLint/issues/5752) +* Align left brace of additional trailing closures with right brace of previous trailing closure + in `contrasted_opening_brace` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5752](https://github.com/realm/SwiftLint/issues/5752) + ## 0.56.1: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift index fdd1a939db..ba4ceb083b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift @@ -80,20 +80,29 @@ private extension BracedSyntax { } if let closure = `as`(ClosureExprSyntax.self), closure.keyPathInParent == \FunctionCallExprSyntax.trailingClosure { - var indentationDecidingToken = closure.leftBrace - repeat { - if let previousToken = indentationDecidingToken.previousToken(viewMode: .sourceAccurate) { - indentationDecidingToken = previousToken - } else { - break - } - } while indentationDecidingToken.leadingTrivia.containsNewlines() == false - return indentationDecidingToken + return closure.leftBrace.previousIndentationDecidingToken + } + if let closureLabel = parent?.as(MultipleTrailingClosureElementSyntax.self)?.label { + return closureLabel.previousIndentationDecidingToken } return parent } } +private extension TokenSyntax { + var previousIndentationDecidingToken: TokenSyntax { + var indentationDecidingToken = self + repeat { + if let previousToken = indentationDecidingToken.previousToken(viewMode: .sourceAccurate) { + indentationDecidingToken = previousToken + } else { + break + } + } while !indentationDecidingToken.leadingTrivia.containsNewlines() + return indentationDecidingToken + } +} + private extension IfExprSyntax { var indentationDecidingParent: any SyntaxProtocol { if keyPathInParent == \IfExprSyntax.elseBody, let parent = parent?.as(IfExprSyntax.self) { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift index 681bc3dd14..5caf1fda74 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRuleExamples.swift @@ -228,6 +228,13 @@ struct ContrastedOpeningBraceRuleExamples { ↓{ $0 } } """), + Example(""" + a ↓{ + $0 + } b: ↓{ + $1 + } + """), ] static let corrections = [ @@ -456,5 +463,20 @@ struct ContrastedOpeningBraceRuleExamples { .c { $1 } """), + Example(""" + a { + $0 + } b: { + $1 + } + """): Example(""" + a + { + $0 + } b: + { + $1 + } + """), ] } From 9d0711070d082b53497994594bd867a30c727351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 20 Aug 2024 22:28:30 +0200 Subject: [PATCH 010/260] Trigger on empty closure blocks in `no_empty_block` rule (#5763) --- CHANGELOG.md | 4 +++ .../Rules/Idiomatic/NoEmptyBlockRule.swift | 29 ++++++++++++++++++- .../NoEmptyBlockConfiguration.swift | 1 + .../Extensions/QueuedPrint.swift | 5 +++- .../Extensions/SwiftLintFile+Cache.swift | 2 +- .../Models/RuleConfigurationDescription.swift | 4 +-- .../NoEmptyBlockConfigurationTests.swift | 2 +- 7 files changed, 41 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 610cd812e5..a2639c4c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5752](https://github.com/realm/SwiftLint/issues/5752) +* Trigger on empty closure blocks in `no_empty_block` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5762](https://github.com/realm/SwiftLint/issues/5762) + ## 0.56.1: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift index 51efc92461..3691a708b6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift @@ -92,6 +92,18 @@ struct NoEmptyBlockRule: OptInRule { while i < 10 {} """, configuration: ["disabled_block_types": ["statement_blocks"]]), + Example(""" + f { _ in /* comment */ } + f { _ in // comment + } + f { _ in + // comment + } + """), + Example(""" + f {} + {}() + """, configuration: ["disabled_block_types": ["closure_blocks"]]), ], triggeringExamples: [ Example(""" @@ -130,6 +142,12 @@ struct NoEmptyBlockRule: OptInRule { while i < 10 ↓{} """), + Example(""" + f ↓{} + """), + Example(""" + Button ↓{} label: ↓{} + """), ] ) } @@ -142,7 +160,14 @@ private extension NoEmptyBlockRule { } } - func validate(node: CodeBlockSyntax) { + override func visitPost(_ node: ClosureExprSyntax) { + if configuration.enabledBlockTypes.contains(.closureBlocks), + node.signature?.inKeyword.trailingTrivia.containsComments != true { + validate(node: node) + } + } + + func validate(node: some BracedSyntax & WithStatementsSyntax) { guard node.statements.isEmpty, !node.leftBrace.trailingTrivia.containsComments, !node.rightBrace.leadingTrivia.containsComments else { @@ -162,6 +187,8 @@ private extension CodeBlockSyntax { .initializerBodies case .forStmt, .doStmt, .whileStmt, .repeatStmt, .ifExpr, .catchClause, .deferStmt: .statementBlocks + case .closureExpr: + .closureBlocks case .guardStmt: // No need to handle this case since Empty Block of `guard` is compile error. nil diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/NoEmptyBlockConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/NoEmptyBlockConfiguration.swift index a09221fc0b..a367a45575 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/NoEmptyBlockConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/NoEmptyBlockConfiguration.swift @@ -9,6 +9,7 @@ struct NoEmptyBlockConfiguration: SeverityBasedRuleConfiguration { case functionBodies = "function_bodies" case initializerBodies = "initializer_bodies" case statementBlocks = "statement_blocks" + case closureBlocks = "closure_blocks" static let all = Set(allCases) } diff --git a/Source/SwiftLintCore/Extensions/QueuedPrint.swift b/Source/SwiftLintCore/Extensions/QueuedPrint.swift index f4be8d4172..bc898c5b5d 100644 --- a/Source/SwiftLintCore/Extensions/QueuedPrint.swift +++ b/Source/SwiftLintCore/Extensions/QueuedPrint.swift @@ -17,7 +17,10 @@ private let outputQueue: DispatchQueue = { private func setupAtExitHandler() { atexit { - outputQueue.sync(flags: .barrier) {} + // Ensure all queued output is written before exiting. + outputQueue.sync(flags: .barrier) { + // Just wait. + } } } diff --git a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift index 26d5175789..65a9bf58bd 100644 --- a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift +++ b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift @@ -29,7 +29,7 @@ private let syntaxTreeCache = Cache { file -> SourceFileSyntax in } private let foldedSyntaxTreeCache = Cache { file -> SourceFileSyntax? in OperatorTable.standardOperators - .foldAll(file.syntaxTree) { _ in } + .foldAll(file.syntaxTree) { _ in /* Don't handle errors. */ } .as(SourceFileSyntax.self) } private let locationConverterCache = Cache { file -> SourceLocationConverter in diff --git a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift index 7ba0dbfae3..b09b4a914e 100644 --- a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift +++ b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift @@ -463,7 +463,7 @@ public struct ConfigurationElement Void = { _ in }) { + postprocessor: @escaping (inout T) -> Void = { _ in }) { // swiftlint:disable:this no_empty_block self.init( wrappedValue: value, key: key, @@ -511,7 +511,7 @@ public struct ConfigurationElement Void = { _ in }) { + postprocessor: @escaping (inout T) -> Void = { _ in }) { // swiftlint:disable:this no_empty_block self.wrappedValue = wrappedValue self.key = key self.inline = inline diff --git a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift index 39168dfd30..851f358587 100644 --- a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift @@ -18,7 +18,7 @@ final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { ] as [String: any Sendable] ) XCTAssertEqual(config.severityConfiguration.severity, .error) - XCTAssertEqual(config.enabledBlockTypes, Set([.initializerBodies, .statementBlocks])) + XCTAssertEqual(config.enabledBlockTypes, Set([.initializerBodies, .statementBlocks, .closureBlocks])) } func testInvalidKeyInCustomConfiguration() { From f3bdd27626f4af93a0c2a313e9306f40ce971bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 21 Aug 2024 18:56:51 +0200 Subject: [PATCH 011/260] Silence `unneeded_override` rule on methods and initializers with attributes (#5764) --- CHANGELOG.md | 4 ++++ .../Rules/Lint/UnneededOverrideRule.swift | 4 ++-- .../Rules/Lint/UnneededOverrideRuleExamples.swift | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2639c4c57..e1381e8fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5762](https://github.com/realm/SwiftLint/issues/5762) +* Silence `unneeded_override` rule on methods and initializers with attributes. + [SimplyDanny](https://github.com/SimplyDanny) + [#5753](https://github.com/realm/SwiftLint/issues/5753) + ## 0.56.1: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift index 58ef48cf16..3a00e4e22f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift @@ -89,8 +89,8 @@ private extension OverridableDecl { return false } - // Assume having @available changes behavior. - if attributes.contains(attributeNamed: "available") { + // Assume attributes change behavior. + guard attributes.isEmpty else { return false } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift index 1109108422..87f0e94947 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift @@ -17,6 +17,13 @@ struct UnneededOverrideRuleExamples { } """), Example(""" + class Foo { + @objc override func bar() { + super.bar() + } + } + """), + Example(""" class Foo { override func bar() { super.bar() From c8a9065428166b9f968ebe7062b7fc2976c1892f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 21 Aug 2024 19:07:39 +0200 Subject: [PATCH 012/260] Silence `prefer_key_path` rule on macro expansion expressions (#5765) --- CHANGELOG.md | 4 ++++ .../Rules/Idiomatic/PreferKeyPathRule.swift | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1381e8fce..853a4778de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5153](https://github.com/realm/SwiftLint/issues/5153) +* Silence `prefer_key_path` rule on macro expansion expressions. + [SimplyDanny](https://github.com/SimplyDanny) + [#5744](https://github.com/realm/SwiftLint/issues/5744) + * Check `if` expressions nested arbitrarily deep in `contrasted_opening_brace` rule. [SimplyDanny](https://github.com/SimplyDanny) [#5752](https://github.com/realm/SwiftLint/issues/5752) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift index d996119a65..127564620d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift @@ -29,6 +29,7 @@ struct PreferKeyPathRule: OptInRule { Example("[1, 2, 3].reduce(1) { $0 + $1 }", configuration: extendedMode), Example("f.map(1) { $0.a }"), Example("f.filter({ $0.a }, x)"), + Example("#Predicate { $0.a }"), ], triggeringExamples: [ Example("f.map ↓{ $0.a }"), @@ -174,10 +175,9 @@ private extension ClosureExprSyntax { } func isInvalid(restrictToStandardFunctions: Bool) -> Bool { - if keyPathInParent == \FunctionCallExprSyntax.calledExpression { - return true - } - if parent?.is(MultipleTrailingClosureElementSyntax.self) == true { + guard keyPathInParent != \FunctionCallExprSyntax.calledExpression, + let parentKind = parent?.kind, + ![.macroExpansionExpr, .multipleTrailingClosureElement].contains(parentKind) else { return true } if let call = parent?.as(LabeledExprSyntax.self)?.parent?.parent?.as(FunctionCallExprSyntax.self) { From a24488f26e60247d8fff7bbb03d51910af3dc91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 22 Aug 2024 13:20:18 +0200 Subject: [PATCH 013/260] Release 0.56.2 --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Package.swift | 4 ++-- Source/SwiftLintCore/Models/Version.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 853a4778de..9c4244e0f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Main +## 0.56.2: Heat Pump Dryer #### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index c1055f246d..0ce621122c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.56.1", + version = "0.56.2", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Package.swift b/Package.swift index 34fec6bfef..22207c767e 100644 --- a/Package.swift +++ b/Package.swift @@ -172,8 +172,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.56.1/SwiftLintBinary-macos.artifactbundle.zip", - checksum: "146ef723e83d301b9f1ef647dc924a55dae293887e633618e76f8cb526292f0c" + url: "https://github.com/realm/SwiftLint/releases/download/0.56.2/SwiftLintBinary-macos.artifactbundle.zip", + checksum: "197df93d7f5041d8cd46d6902a34ad57914efe1b5b50635995f3b9065f2c3ffd" ) ) #endif diff --git a/Source/SwiftLintCore/Models/Version.swift b/Source/SwiftLintCore/Models/Version.swift index 0edafff9d3..60cbe4057f 100644 --- a/Source/SwiftLintCore/Models/Version.swift +++ b/Source/SwiftLintCore/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable { } /// The current SwiftLint version. - public static let current = Self(value: "0.56.1") + public static let current = Self(value: "0.56.2") /// Public initializer. /// From fb8551f3a3866d52b7c9b6f7fb8303f71b4cfac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 22 Aug 2024 13:23:07 +0200 Subject: [PATCH 014/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4244e0f1..32573e71ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## Main + +#### Breaking + +* None. + +#### Experimental + +* None. + +#### Enhancements + +* None. + +#### Bug Fixes + +* None. + ## 0.56.2: Heat Pump Dryer #### Breaking From 7d539acac6ac0ae0dfd1a622ee93e730e07d2e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 22 Aug 2024 13:46:20 +0200 Subject: [PATCH 015/260] Add fixed releaser It should actually work without this config as long as the releaser is not a bot. However, the BCR publishing step wasn't triggered. So perhaps this config is required ... We'll see in the next round. --- .bcr/config.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .bcr/config.yml diff --git a/.bcr/config.yml b/.bcr/config.yml new file mode 100644 index 0000000000..c35c48cf09 --- /dev/null +++ b/.bcr/config.yml @@ -0,0 +1,3 @@ +fixedReleaser: + login: SimplyDanny + email: danny.moesch@icloud.com From 13315081ae2fcb8fc37c09c2464e1148cb9526d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:18:32 -0400 Subject: [PATCH 016/260] Bump rexml from 3.3.3 to 3.3.6 (#5767) Bumps the bundler group with 1 update in the / directory: [rexml](https://github.com/ruby/rexml). Updates `rexml` from 3.3.3 to 3.3.6 - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.3...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect dependency-group: bundler ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f09505a870..5e6967364c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,7 +122,7 @@ GEM public_suffix (4.0.7) rchardet (1.8.0) redcarpet (3.6.0) - rexml (3.3.3) + rexml (3.3.6) strscan rouge (4.3.0) ruby-macho (2.5.1) From 9f4cb9240bb516b3589f3f0aa576230cad0c3320 Mon Sep 17 00:00:00 2001 From: ikelax <163678144+ikelax@users.noreply.github.com> Date: Fri, 23 Aug 2024 17:48:38 +0200 Subject: [PATCH 017/260] Swift type checking using is (#5561) --- CHANGELOG.md | 5 ++ .../Models/BuiltInRules.swift | 1 + .../Idiomatic/PreferTypeCheckingRule.swift | 87 +++++++++++++++++++ Tests/GeneratedTests/GeneratedTests.swift | 6 ++ .../default_rule_configurations.yml | 2 + 5 files changed, 101 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 32573e71ab..928ff5cfba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -475,6 +475,11 @@ `let s: String = ""` as redundant. [Garric Nahapetian](https://github.com/garricn) +* Add new `prefer_type_checking` rule to prefer `a is X` over `a as? X != nil`. + [ikelax](https://github.com/ikelax) + [mildm8nnered](https://github.com/mildm8nnered) + [#5295](https://github.com/realm/SwiftLint/issues/5295) + #### Bug Fixes * Invalid keys in a configuration don't lead to the default configuration being diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 86105239e6..06613a00ad 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -152,6 +152,7 @@ public let builtInRules: [any Rule.Type] = [ PreferNimbleRule.self, PreferSelfInStaticReferencesRule.self, PreferSelfTypeOverTypeOfSelfRule.self, + PreferTypeCheckingRule.self, PreferZeroOverExplicitInitRule.self, PrefixedTopLevelConstantRule.self, PrivateActionRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift new file mode 100644 index 0000000000..c0ae6731d7 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift @@ -0,0 +1,87 @@ +import SwiftSyntax +import SwiftSyntaxBuilder + +@SwiftSyntaxRule(foldExpressions: true, explicitRewriter: true) +struct PreferTypeCheckingRule: Rule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "prefer_type_checking", + name: "Prefer Type Checking", + description: "Prefer `a is X` to `a as? X != nil`", + kind: .idiomatic, + nonTriggeringExamples: [ + Example("let foo = bar as? Foo"), + Example("bar is Foo"), + Example("2*x is X"), + Example(""" + if foo is Bar { + doSomeThing() + } + """), + Example(""" + if let bar = foo as? Bar { + foo.run() + } + """), + ], + triggeringExamples: [ + Example("bar ↓as? Foo != nil"), + Example("2*x as? X != nil"), + Example(""" + if foo ↓as? Bar != nil { + doSomeThing() + } + """), + ], + corrections: [ + Example("bar ↓as? Foo != nil"): Example("bar is Foo"), + Example("2*x ↓as? X != nil"): Example("2*x is X"), + Example(""" + if foo ↓as? Bar != nil { + doSomeThing() + } + """): Example(""" + if foo is Bar { + doSomeThing() + } + """), + ] + ) +} + +private extension PreferTypeCheckingRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: InfixOperatorExprSyntax) { + if node.typeChecksWithAsCasting, let asExpr = node.leftOperand.as(AsExprSyntax.self) { + violations.append(asExpr.asKeyword.positionAfterSkippingLeadingTrivia) + } + } + } + + final class Rewriter: ViolationsSyntaxRewriter { + override func visit(_ node: InfixOperatorExprSyntax) -> ExprSyntax { + guard node.typeChecksWithAsCasting, + let asExpr = node.leftOperand.as(AsExprSyntax.self) else { + return super.visit(node) + } + + correctionPositions.append(asExpr.asKeyword.positionAfterSkippingLeadingTrivia) + + let expression = asExpr.expression.trimmed + let type = asExpr.type.trimmed + + return ExprSyntax(stringLiteral: "\(expression) is \(type)") + .with(\.leadingTrivia, node.leadingTrivia) + .with(\.trailingTrivia, node.trailingTrivia) + } + } +} + +private extension InfixOperatorExprSyntax { + var typeChecksWithAsCasting: Bool { + self.leftOperand.is(AsExprSyntax.self) + && self.operator.as(BinaryOperatorExprSyntax.self)?.operator.tokenKind == .binaryOperator("!=") + && self.rightOperand.is(NilLiteralExprSyntax.self) + } +} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 2c212346f3..d46eff77b4 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -901,6 +901,12 @@ final class PreferSelfTypeOverTypeOfSelfRuleGeneratedTests: SwiftLintTestCase { } } +final class PreferTypeCheckingRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(PreferTypeCheckingRule.description) + } +} + final class PreferZeroOverExplicitInitRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(PreferZeroOverExplicitInitRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 3edd735e5a..0c75d9ac43 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -408,6 +408,8 @@ prefer_self_in_static_references: severity: warning prefer_self_type_over_type_of_self: severity: warning +prefer_type_checking: + severity: warning prefer_zero_over_explicit_init: severity: warning prefixed_toplevel_constant: From 60a1d342f43808a9d669cd53706688f9d717b970 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Sat, 24 Aug 2024 09:51:04 +0100 Subject: [PATCH 018/260] Remove `anyobject_protocol` rule (#5770) --- .swiftlint.yml | 1 - CHANGELOG.md | 4 +- .../Models/BuiltInRules.swift | 1 - .../Rules/Lint/AnyObjectProtocolRule.swift | 77 ------------------- Tests/GeneratedTests/GeneratedTests.swift | 6 -- .../default_rule_configurations.yml | 2 - 6 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/AnyObjectProtocolRule.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 05b56bc77a..8dc4c564ed 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -15,7 +15,6 @@ opt_in_rules: - all disabled_rules: - anonymous_argument_in_multiline_closure - - anyobject_protocol - conditional_returns_on_newline - contrasted_opening_brace - convenience_type diff --git a/CHANGELOG.md b/CHANGELOG.md index 928ff5cfba..e42e2ca5f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ #### Breaking -* None. +* The deprecated `anyobject_protocol` rule has now been removed. + [Martin Redington](https://github.com/mildm8nnered) + [#5769](https://github.com/realm/SwiftLint/issues/5769) #### Experimental diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 06613a00ad..73f981ab41 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -6,7 +6,6 @@ public let builtInRules: [any Rule.Type] = [ AccessibilityLabelForImageRule.self, AccessibilityTraitForButtonRule.self, AnonymousArgumentInMultilineClosureRule.self, - AnyObjectProtocolRule.self, ArrayInitRule.self, AttributesRule.self, BalancedXCTestLifecycleRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AnyObjectProtocolRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AnyObjectProtocolRule.swift deleted file mode 100644 index 3d8ded4ae5..0000000000 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AnyObjectProtocolRule.swift +++ /dev/null @@ -1,77 +0,0 @@ -import SwiftSyntax - -// TODO: [09/07/2024] Remove deprecation warning after ~2 years. -private let warnDeprecatedOnceImpl: Void = { - Issue.ruleDeprecated(ruleID: AnyObjectProtocolRule.description.identifier).print() -}() - -private func warnDeprecatedOnce() { - _ = warnDeprecatedOnceImpl -} - -struct AnyObjectProtocolRule: SwiftSyntaxCorrectableRule, OptInRule { - var configuration = SeverityConfiguration(.warning) - - static let description = RuleDescription( - identifier: "anyobject_protocol", - name: "AnyObject Protocol", - description: "Prefer using `AnyObject` over `class` for class-only protocols", - kind: .lint, - nonTriggeringExamples: [ - Example("protocol SomeProtocol {}"), - Example("protocol SomeClassOnlyProtocol: AnyObject {}"), - Example("protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {}"), - Example("@objc protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {}"), - ], - triggeringExamples: [ - Example("protocol SomeClassOnlyProtocol: ↓class {}"), - Example("protocol SomeClassOnlyProtocol: ↓class, SomeInheritedProtocol {}"), - Example("@objc protocol SomeClassOnlyProtocol: ↓class, SomeInheritedProtocol {}"), - ], - corrections: [ - Example("protocol SomeClassOnlyProtocol: ↓class {}"): - Example("protocol SomeClassOnlyProtocol: AnyObject {}"), - Example("protocol SomeClassOnlyProtocol: ↓class, SomeInheritedProtocol {}"): - Example("protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {}"), - Example("@objc protocol SomeClassOnlyProtocol: ↓class, SomeInheritedProtocol {}"): - Example("@objc protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {}"), - ] - ) - - func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor { - warnDeprecatedOnce() - return Visitor(configuration: configuration, file: file) - } - - func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? { - Rewriter(configuration: configuration, file: file) - } -} - -private extension AnyObjectProtocolRule { - final class Visitor: ViolationsSyntaxVisitor { - override func visitPost(_ node: ClassRestrictionTypeSyntax) { - violations.append(node.positionAfterSkippingLeadingTrivia) - } - } - - final class Rewriter: ViolationsSyntaxRewriter { - override func visit(_ node: InheritedTypeSyntax) -> InheritedTypeSyntax { - let typeName = node.type - guard typeName.is(ClassRestrictionTypeSyntax.self) else { - return super.visit(node) - } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - return super.visit( - node.with( - \.type, - TypeSyntax( - IdentifierTypeSyntax(name: .identifier("AnyObject"), genericArgumentClause: nil) - .with(\.leadingTrivia, typeName.leadingTrivia) - .with(\.trailingTrivia, typeName.trailingTrivia) - ) - ) - ) - } - } -} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index d46eff77b4..1b506913c1 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -25,12 +25,6 @@ final class AnonymousArgumentInMultilineClosureRuleGeneratedTests: SwiftLintTest } } -final class AnyObjectProtocolRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(AnyObjectProtocolRule.description) - } -} - final class ArrayInitRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(ArrayInitRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 0c75d9ac43..969ca71aa8 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -4,8 +4,6 @@ accessibility_trait_for_button: severity: warning anonymous_argument_in_multiline_closure: severity: warning -anyobject_protocol: - severity: warning array_init: severity: warning attributes: From 3bb80147822850fb82f0044fa6512ab729720106 Mon Sep 17 00:00:00 2001 From: Leonardo de Sousa Rodrigues <63623604+leonardosrodrigues0@users.noreply.github.com> Date: Sat, 24 Aug 2024 19:44:33 +1000 Subject: [PATCH 019/260] Add options to `opening_brace` rule that silence it on multiline statements and types (#5521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 7 +- .../OpeningBraceConfiguration.swift | 14 +- .../Rules/Style/OpeningBraceRule.swift | 112 +++++++++++++-- .../default_rule_configurations.yml | 3 + .../OpeningBraceRuleTests.swift | 132 ++++++++++++++++-- 5 files changed, 241 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e42e2ca5f3..72f6c71a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,12 @@ #### Enhancements -* None. +* Add `ignore_multiline_type_headers` and `ignore_multiline_statement_conditions` + options to `opening_brace` rule to allow opening braces to be on a new line after + multiline type headers or statement conditions. Rename `allow_multiline_func` to + `ignore_multiline_function_signatures`. + [leonardosrodrigues0](https://github.com/leonardosrodrigues0) + [#3720](https://github.com/realm/SwiftLint/issues/3720) #### Bug Fixes diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/OpeningBraceConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/OpeningBraceConfiguration.swift index 1e72688009..a136f1d300 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/OpeningBraceConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/OpeningBraceConfiguration.swift @@ -6,6 +6,18 @@ struct OpeningBraceConfiguration: SeverityBasedRuleConfiguration { @ConfigurationElement(key: "severity") private(set) var severityConfiguration = SeverityConfiguration(.warning) - @ConfigurationElement(key: "allow_multiline_func") + @ConfigurationElement(key: "ignore_multiline_type_headers") + private(set) var ignoreMultilineTypeHeaders = false + @ConfigurationElement(key: "ignore_multiline_statement_conditions") + private(set) var ignoreMultilineStatementConditions = false + @ConfigurationElement(key: "ignore_multiline_function_signatures") + private(set) var ignoreMultilineFunctionSignatures = false + // TODO: [08/23/2026] Remove deprecation warning after ~2 years. + @ConfigurationElement(key: "allow_multiline_func", deprecationNotice: .suggestAlternative( + ruleID: Parent.identifier, name: "ignore_multiline_function_signatures")) private(set) var allowMultilineFunc = false + + var shouldIgnoreMultilineFunctionSignatures: Bool { + ignoreMultilineFunctionSignatures || allowMultilineFunc + } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift index 3dbbaeb9ef..4e3e46b864 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift @@ -25,31 +25,123 @@ struct OpeningBraceRule: SwiftSyntaxCorrectableRule { private extension OpeningBraceRule { final class Visitor: CodeBlockVisitor { - override func visitPost(_ node: FunctionDeclSyntax) { - guard let body = node.body else { + // MARK: - Type Declarations + + override func visitPost(_ node: ActorDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.actorKeyword) { return } - if configuration.allowMultilineFunc, refersToMultilineFunction(body, functionIndicator: node.funcKeyword) { + + super.visitPost(node) + } + + override func visitPost(_ node: ClassDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.classKeyword) { return } - collectViolations(for: body) + + super.visitPost(node) } - override func visitPost(_ node: InitializerDeclSyntax) { - guard let body = node.body else { + override func visitPost(_ node: EnumDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.enumKeyword) { + return + } + + super.visitPost(node) + } + + override func visitPost(_ node: ExtensionDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.extensionKeyword) { + return + } + + super.visitPost(node) + } + + override func visitPost(_ node: ProtocolDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.protocolKeyword) { return } - if configuration.allowMultilineFunc, refersToMultilineFunction(body, functionIndicator: node.initKeyword) { + + super.visitPost(node) + } + + override func visitPost(_ node: StructDeclSyntax) { + if configuration.ignoreMultilineTypeHeaders, + hasMultilinePredecessors(node.memberBlock, keyword: node.structKeyword) { return } - collectViolations(for: body) + + super.visitPost(node) } - private func refersToMultilineFunction(_ body: CodeBlockSyntax, functionIndicator: TokenSyntax) -> Bool { + // MARK: - Conditional Statements + + override func visitPost(_ node: ForStmtSyntax) { + if configuration.ignoreMultilineStatementConditions, + hasMultilinePredecessors(node.body, keyword: node.forKeyword) { + return + } + + super.visitPost(node) + } + + override func visitPost(_ node: IfExprSyntax) { + if configuration.ignoreMultilineStatementConditions, + hasMultilinePredecessors(node.body, keyword: node.ifKeyword) { + return + } + + super.visitPost(node) + } + + override func visitPost(_ node: WhileStmtSyntax) { + if configuration.ignoreMultilineStatementConditions, + hasMultilinePredecessors(node.body, keyword: node.whileKeyword) { + return + } + + super.visitPost(node) + } + + // MARK: - Functions and Initializers + + override func visitPost(_ node: FunctionDeclSyntax) { + if let body = node.body, + configuration.shouldIgnoreMultilineFunctionSignatures, + hasMultilinePredecessors(body, keyword: node.funcKeyword) { + return + } + + super.visitPost(node) + } + + override func visitPost(_ node: InitializerDeclSyntax) { + if let body = node.body, + configuration.shouldIgnoreMultilineFunctionSignatures, + hasMultilinePredecessors(body, keyword: node.initKeyword) { + return + } + + super.visitPost(node) + } + + // MARK: - Other Methods + + /// Checks if a `BracedSyntax` has a multiline predecessor. + /// For type declarations, the predecessor is the header. For conditional statements, + /// it is the condition list, and for functions, it is the signature. + private func hasMultilinePredecessors(_ body: some BracedSyntax, keyword: TokenSyntax) -> Bool { guard let endToken = body.previousToken(viewMode: .sourceAccurate) else { return false } - let startLocation = functionIndicator.endLocation(converter: locationConverter) + let startLocation = keyword.endLocation(converter: locationConverter) let endLocation = endToken.endLocation(converter: locationConverter) let braceLocation = body.leftBrace.endLocation(converter: locationConverter) return startLocation.line != endLocation.line && endLocation.line != braceLocation.line diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 969ca71aa8..386bb8a6c0 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -375,6 +375,9 @@ one_declaration_per_file: severity: warning opening_brace: severity: warning + ignore_multiline_type_headers: false + ignore_multiline_statement_conditions: false + ignore_multiline_function_signatures: false allow_multiline_func: false operator_usage_whitespace: severity: warning diff --git a/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift b/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift index afbf8cf29c..445b2afd44 100644 --- a/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift @@ -1,25 +1,120 @@ @testable import SwiftLintBuiltInRules final class OpeningBraceRuleTests: SwiftLintTestCase { - func testDefaultExamplesRunInMultilineMode() { + func testDefaultNonTriggeringExamplesWithMultilineOptionsTrue() { let description = OpeningBraceRule.description - .with(triggeringExamples: OpeningBraceRule.description.triggeringExamples.removing([ - Example("func abc(a: A,\n\tb: B)\n↓{"), - Example(""" - internal static func getPointer() - -> UnsafeMutablePointer<_ThreadLocalStorage> - ↓{ - return _swift_stdlib_threadLocalStorageGet().assumingMemoryBound( - to: _ThreadLocalStorage.self) - } - """), - ])) + .with(triggeringExamples: []) + .with(corrections: [:]) + + verifyRule(description, ruleConfiguration: [ + "ignore_multiline_statement_conditions": true, + "ignore_multiline_type_headers": true, + "ignore_multiline_function_signatures": true, + ]) + } + + func testWithIgnoreMultilineTypeHeadersTrue() { + let nonTriggeringExamples = [ + Example(""" + extension A + where B: Equatable + {} + """), + Example(""" + struct S: Comparable, + Identifiable + { + init() {} + } + """), + ] + + let triggeringExamples = [ + Example(""" + struct S + ↓{} + """), + Example(""" + extension A where B: Equatable + ↓{ + + } + """), + Example(""" + class C + // with comments + ↓{} + """), + ] + + let description = OpeningBraceRule.description + .with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + .with(corrections: [:]) + + verifyRule(description, ruleConfiguration: ["ignore_multiline_type_headers": true]) + } + + func testWithIgnoreMultilineStatementConditionsTrue() { + let nonTriggeringExamples = [ + Example(""" + while + abc + {} + """), + Example(""" + if x { + + } else if + y, + z + { + + } + """), + Example(""" + if + condition1, + let var1 = var1 + {} + """), + ] + + let triggeringExamples = [ + Example(""" + if x + ↓{} + """), + Example(""" + if x { - verifyRule(description, ruleConfiguration: ["allow_multiline_func": true]) + } else if y, z + ↓{} + """), + Example(""" + if x { + + } else + ↓{} + """), + Example(""" + while abc + // comments + ↓{ + } + """), + ] + + let description = OpeningBraceRule.description + .with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + .with(corrections: [:]) + + verifyRule(description, ruleConfiguration: ["ignore_multiline_statement_conditions": true]) } // swiftlint:disable:next function_body_length - func testWithAllowMultilineTrue() { + func testWithIgnoreMultilineFunctionSignaturesTrue() { let nonTriggeringExamples = [ Example(""" func abc( @@ -80,6 +175,13 @@ final class OpeningBraceRuleTests: SwiftLintTestCase { } } """), + Example(""" + class C { + init(a: Int) + // with comments + ↓{} + } + """), ] let description = OpeningBraceRule.description @@ -87,7 +189,7 @@ final class OpeningBraceRuleTests: SwiftLintTestCase { .with(triggeringExamples: triggeringExamples) .with(corrections: [:]) - verifyRule(description, ruleConfiguration: ["allow_multiline_func": true]) + verifyRule(description, ruleConfiguration: ["ignore_multiline_function_signatures": true]) } } From 48aaca61f05669b0eee8152866ddf68c52c40643 Mon Sep 17 00:00:00 2001 From: Sam Rayner Date: Sat, 24 Aug 2024 11:13:04 +0100 Subject: [PATCH 020/260] Reverse Data -> String conversion rule (#5601) --- CHANGELOG.md | 13 ++++++++ .../Models/BuiltInRules.swift | 1 + .../NonOptionalStringDataConversionRule.swift | 19 +++-------- .../OptionalDataStringConversionRule.swift | 32 +++++++++++++++++++ .../Extensions/Configuration+Remote.swift | 2 +- .../RegexConfiguration.swift | 5 +-- .../Configuration+CommandLine.swift | 6 ++-- .../Helpers/LintableFilesVisitor.swift | 4 +-- .../Helpers/SwiftPMCompilationDB.swift | 2 +- Tests/GeneratedTests/GeneratedTests.swift | 6 ++++ Tests/IntegrationTests/IntegrationTests.swift | 4 +-- .../default_rule_configurations.yml | 2 ++ 12 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 72f6c71a63..32afe3b03d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ [Martin Redington](https://github.com/mildm8nnered) [#5769](https://github.com/realm/SwiftLint/issues/5769) +* Revert the part of the `non_optional_string_data_conversion` + rule that enforces non-failable conversions of `Data` to UTF-8 + `String`. This is due to the fact that the data to be converted + can be arbitrary and especially doesn't need to represent a valid + UTF-8-encoded string. + [Sam Rayner](https://github.com/samrayner) + [#5263](https://github.com/realm/SwiftLint/issues/5263) + #### Experimental * None. @@ -19,6 +27,11 @@ [leonardosrodrigues0](https://github.com/leonardosrodrigues0) [#3720](https://github.com/realm/SwiftLint/issues/3720) +* Add new `optional_data_string_conversion` rule to enforce + failable conversions of `Data` to UTF-8 `String`. + [Sam Rayner](https://github.com/samrayner) + [#5263](https://github.com/realm/SwiftLint/issues/5263) + #### Bug Fixes * None. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 73f981ab41..121c9e6032 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -141,6 +141,7 @@ public let builtInRules: [any Rule.Type] = [ OpeningBraceRule.self, OperatorFunctionWhitespaceRule.self, OperatorUsageWhitespaceRule.self, + OptionalDataStringConversionRule.self, OptionalEnumCaseMatchingRule.self, OrphanedDocCommentRule.self, OverriddenSuperCallRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/NonOptionalStringDataConversionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/NonOptionalStringDataConversionRule.swift index 2e83b87bde..5141717370 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/NonOptionalStringDataConversionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/NonOptionalStringDataConversionRule.swift @@ -5,16 +5,14 @@ struct NonOptionalStringDataConversionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( identifier: "non_optional_string_data_conversion", - name: "Non-Optional String <-> Data Conversion", - description: "Prefer using UTF-8 encoded strings when converting between `String` and `Data`", + name: "Non-optional String -> Data Conversion", + description: "Prefer non-optional `Data(_:)` initializer when converting `String` to `Data`", kind: .lint, nonTriggeringExamples: [ - Example("Data(\"foo\".utf8)"), - Example("String(decoding: data, as: UTF8.self)"), + Example("Data(\"foo\".utf8)") ], triggeringExamples: [ - Example("\"foo\".data(using: .utf8)"), - Example("String(data: data, encoding: .utf8)"), + Example("\"foo\".data(using: .utf8)") ] ) } @@ -31,15 +29,6 @@ private extension NonOptionalStringDataConversionRule { violations.append(node.positionAfterSkippingLeadingTrivia) } } - - override func visitPost(_ node: DeclReferenceExprSyntax) { - if node.baseName.text == "String", - let parent = node.parent?.as(FunctionCallExprSyntax.self), - parent.arguments.map({ $0.label?.text }) == ["data", "encoding"], - parent.arguments.last?.expression.as(MemberAccessExprSyntax.self)?.isUTF8 == true { - violations.append(node.positionAfterSkippingLeadingTrivia) - } - } } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift new file mode 100644 index 0000000000..a98b6dddf9 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift @@ -0,0 +1,32 @@ +import SwiftSyntax + +@SwiftSyntaxRule +struct OptionalDataStringConversionRule: Rule { + var configuration = SeverityConfiguration(.warning) + static let description = RuleDescription( + identifier: "optional_data_string_conversion", + name: "Optional Data -> String Conversion", + description: "Prefer failable `String(data:encoding:)` initializer when converting `Data` to `String`", + kind: .lint, + nonTriggeringExamples: [ + Example("String(data: data, encoding: .utf8)") + ], + triggeringExamples: [ + Example("String(decoding: data, as: UTF8.self)") + ] + ) +} + +private extension OptionalDataStringConversionRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: DeclReferenceExprSyntax) { + if node.baseName.text == "String", + let parent = node.parent?.as(FunctionCallExprSyntax.self), + let expr = parent.arguments.last?.expression.as(MemberAccessExprSyntax.self), + expr.base?.description == "UTF8", + expr.declName.baseName.description == "self" { + violations.append(node.positionAfterSkippingLeadingTrivia) + } + } + } +} diff --git a/Source/SwiftLintCore/Extensions/Configuration+Remote.swift b/Source/SwiftLintCore/Extensions/Configuration+Remote.swift index 3337ad1da0..e8eec05c86 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Remote.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Remote.swift @@ -94,7 +94,7 @@ internal extension Configuration.FileGraph.FilePath { guard taskResult.2 == nil, // No error (taskResult.1 as? HTTPURLResponse)?.statusCode == 200, - let configStr = (taskResult.0.flatMap { String(decoding: $0, as: UTF8.self) }) + let configStr = (taskResult.0.flatMap { String(data: $0, encoding: .utf8) }) else { return try handleWrongData( urlString: urlString, diff --git a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift index e44c25a307..65b44876f6 100644 --- a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift +++ b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift @@ -37,8 +37,9 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, .map(\.rawValue).sorted(by: <).joined(separator: ","), severity.rawValue, ] - if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject) { - return String(decoding: jsonData, as: UTF8.self) + if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject), + let jsonString = String(data: jsonData, encoding: .utf8) { + return jsonString } queuedFatalError("Could not serialize regex configuration for cache") } diff --git a/Source/swiftlint/Extensions/Configuration+CommandLine.swift b/Source/swiftlint/Extensions/Configuration+CommandLine.swift index 84a7119374..6eb4eeb061 100644 --- a/Source/swiftlint/Extensions/Configuration+CommandLine.swift +++ b/Source/swiftlint/Extensions/Configuration+CommandLine.swift @@ -213,8 +213,10 @@ extension Configuration { fileprivate func getFiles(with visitor: LintableFilesVisitor) async throws -> [SwiftLintFile] { if visitor.useSTDIN { let stdinData = FileHandle.standardInput.readDataToEndOfFile() - let stdinString = String(decoding: stdinData, as: UTF8.self) - return [SwiftLintFile(contents: stdinString)] + if let stdinString = String(data: stdinData, encoding: .utf8) { + return [SwiftLintFile(contents: stdinString)] + } + throw SwiftLintError.usageError(description: "stdin isn't a UTF8-encoded string") } if visitor.useScriptInputFiles { let files = try scriptInputFiles() diff --git a/Source/swiftlint/Helpers/LintableFilesVisitor.swift b/Source/swiftlint/Helpers/LintableFilesVisitor.swift index c11d11688d..c200513478 100644 --- a/Source/swiftlint/Helpers/LintableFilesVisitor.swift +++ b/Source/swiftlint/Helpers/LintableFilesVisitor.swift @@ -187,8 +187,8 @@ struct LintableFilesVisitor { } private static func loadLogCompilerInvocations(_ path: String) -> [[String]]? { - if let data = FileManager.default.contents(atPath: path) { - let logContents = String(decoding: data, as: UTF8.self) + if let data = FileManager.default.contents(atPath: path), + let logContents = String(data: data, encoding: .utf8) { if logContents.isEmpty { return nil } diff --git a/Source/swiftlint/Helpers/SwiftPMCompilationDB.swift b/Source/swiftlint/Helpers/SwiftPMCompilationDB.swift index 238985f167..d651290d48 100644 --- a/Source/swiftlint/Helpers/SwiftPMCompilationDB.swift +++ b/Source/swiftlint/Helpers/SwiftPMCompilationDB.swift @@ -37,7 +37,7 @@ struct SwiftPMCompilationDB: Codable { let pathToReplace = Array(nodes.nodes.keys.filter({ node in node.hasSuffix(suffix) }))[0].dropLast(suffix.count - 1) - let stringFileContents = String(decoding: yaml, as: UTF8.self) + let stringFileContents = String(data: yaml, encoding: .utf8)! .replacingOccurrences(of: pathToReplace, with: "") compilationDB = try decoder.decode(Self.self, from: stringFileContents) } else { diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 1b506913c1..332102c95f 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -835,6 +835,12 @@ final class OperatorUsageWhitespaceRuleGeneratedTests: SwiftLintTestCase { } } +final class OptionalDataStringConversionRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(OptionalDataStringConversionRule.description) + } +} + final class OptionalEnumCaseMatchingRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(OptionalEnumCaseMatchingRule.description) diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index a4e345af7f..bbb8318ee9 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -190,8 +190,8 @@ private func execute(_ args: [String], queue.async(group: group) { stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile() } process.waitUntilExit() group.wait() - let stdout = stdoutData.map { String(decoding: $0, as: UTF8.self) } ?? "" - let stderr = stderrData.map { String(decoding: $0, as: UTF8.self) } ?? "" + let stdout = stdoutData.flatMap { String(data: $0, encoding: .utf8) } ?? "" + let stderr = stderrData.flatMap { String(data: $0, encoding: .utf8) } ?? "" return (process.terminationStatus, stdout, stderr) } diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 386bb8a6c0..88e14517c1 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -386,6 +386,8 @@ operator_usage_whitespace: allowed_no_space_operators: ["...", "..<"] operator_whitespace: severity: warning +optional_data_string_conversion: + severity: warning optional_enum_case_matching: severity: warning orphaned_doc_comment: From dfd19bddc20538e4b151927556ea542c8fb021db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 25 Aug 2024 09:11:31 +0200 Subject: [PATCH 021/260] Update Bazel pre-submit steps This is the version of the file that was used to add 0.56.2 to BCR. --- .bcr/presubmit.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 80a746c9ad..0ca2eb510b 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -1,25 +1,26 @@ -shell_commands: &shell_commands -- "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" -- "mkdir $SWIFT_HOME" -- "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" - tasks: verify_targets_linux: - name: Verify targets (Linux) + name: Verify Targets (Linux) platform: ubuntu2004 + bazel: 7.x environment: CC: "clang" SWIFT_VERSION: "5.10" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$PATH:$SWIFT_HOME/usr/bin" - shell_commands: *shell_commands + shell_commands: + - "echo --- Downloading and extracting Swift $SWIFT_VERSION to $SWIFT_HOME" + - "mkdir $SWIFT_HOME" + - "curl https://download.swift.org/swift-${SWIFT_VERSION}-release/ubuntu2004/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu20.04.tar.gz | tar xvz --strip-components=1 -C $SWIFT_HOME" build_flags: - "--action_env=PATH" build_targets: - # TODO: Build `:swiftlint` target when the Swift compiler crash is fixed - - '@swiftlint//:SwiftLintFramework' + - '@swiftlint//:swiftlint' verify_targets_macos: - name: Verify targets (macOS) + name: Verify Targets (macOS) platform: macos + bazel: 7.x build_targets: - '@swiftlint//:swiftlint' + build_flags: + - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" From 94d4f7acf9968014679bfe06ce0448823141b4af Mon Sep 17 00:00:00 2001 From: Hui Xu Date: Fri, 30 Aug 2024 23:25:30 -0700 Subject: [PATCH 022/260] Add instructions for installation in Chinese (#5777) --- README_CN.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README_CN.md b/README_CN.md index 4b622bbbb6..a1593f6e51 100644 --- a/README_CN.md +++ b/README_CN.md @@ -13,6 +13,33 @@ SwiftLint Hook 了 [Clang](http://clang.llvm.org) 和 [SourceKit](http://www.jps 不可接受的行为报告给 [info@realm.io](mailto:info@realm.io)。 ## 安装 +### 使用[Swift Package Manager](https://github.com/apple/swift-package-manager) + +SwiftLint 可以用作[命令插件](#swift-package-command-plugin)或[构建工具插件](#build-tool-plugins) + +添加 + +```swift +.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "") +``` + +到你的 `Package.swift` 文件中,以自动获取 SwiftLint 的最新版本,或者将依赖项固定到特定版本: + +```swift +.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "") +``` + +其中,用所需的最低版本或精确版本替换 ``。 + + +### [Xcode Package Dependency](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) + +使用以下链接将 SwiftLint 作为包依赖添加到 Xcode 项目中: + +```bash +https://github.com/SimplyDanny/SwiftLintPlugins +``` + ### 使用 [Homebrew](http://brew.sh/): From ae3f0c4df9d35b15131bfcabe86f3558101ef8f1 Mon Sep 17 00:00:00 2001 From: Aryaman Sharda Date: Fri, 6 Sep 2024 13:26:47 +0100 Subject: [PATCH 023/260] Add new `attribute_name_spacing` rule (#5669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 6 + .../Models/BuiltInRules.swift | 1 + .../Style/AttributeNameSpacingRule.swift | 158 ++++++++++++++++++ Tests/GeneratedTests/GeneratedTests.swift | 6 + .../default_rule_configurations.yml | 2 + 5 files changed, 173 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 32afe3b03d..65d7db8b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,12 @@ #### Enhancements +* Add new `attribute_name_spacing` rule to enforce no trailing whitespace between + attribute names and parentheses, ensuring compatibility with Swift 6, where this spacing + causes compilation errors. + [aryamansharda](https://github.com/aryamansharda) + [#5667](https://github.com/realm/SwiftLint/issues/5667) + * Linting got up to 30% faster due to the praisworthy performance improvements done in the [SwiftSyntax](https://github.com/swiftlang/swift-syntax) library. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 121c9e6032..eeb3abce7c 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -7,6 +7,7 @@ public let builtInRules: [any Rule.Type] = [ AccessibilityTraitForButtonRule.self, AnonymousArgumentInMultilineClosureRule.self, ArrayInitRule.self, + AttributeNameSpacingRule.self, AttributesRule.self, BalancedXCTestLifecycleRule.self, BlanketDisableCommandRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift new file mode 100644 index 0000000000..50ca9d4b92 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift @@ -0,0 +1,158 @@ +import SwiftLintCore +import SwiftSyntax + +@SwiftSyntaxRule +struct AttributeNameSpacingRule: SwiftSyntaxCorrectableRule { + var configuration = SeverityConfiguration(.error) + + static let description = RuleDescription( + identifier: "attribute_name_spacing", + name: "Attribute Name Spacing", + description: """ + This rule prevents trailing spaces after attribute names, ensuring compatibility \ + with Swift 6 where a space between an attribute name and the opening parenthesis \ + results in a compilation error (e.g. `@MyPropertyWrapper ()`, `private (set)`). + """, + kind: .style, + nonTriggeringExamples: [ + Example("private(set) var foo: Bool = false"), + Example("fileprivate(set) var foo: Bool = false"), + Example("@MainActor class Foo {}"), + Example("func funcWithEscapingClosure(_ x: @escaping () -> Int) {}"), + Example("@available(*, deprecated)"), + Example("@MyPropertyWrapper(param: 2) "), + Example("nonisolated(unsafe) var _value: X?"), + Example("@testable import SwiftLintCore"), + Example("func func_type_attribute_with_space(x: @convention(c) () -> Int) {}"), + Example(""" + @propertyWrapper + struct MyPropertyWrapper { + var wrappedValue: Int = 1 + + init(param: Int) {} + } + """), + Example(""" + let closure2 = { @MainActor + (a: Int, b: Int) in + } + """), + ], + triggeringExamples: [ + Example("private ↓(set) var foo: Bool = false"), + Example("fileprivate ↓(set) var foo: Bool = false"), + Example("public ↓(set) var foo: Bool = false"), + Example(" public ↓(set) var foo: Bool = false"), + Example("@ ↓MainActor class Foo {}"), + Example("func funcWithEscapingClosure(_ x: @ ↓escaping () -> Int) {}"), + Example("func funcWithEscapingClosure(_ x: @escaping↓() -> Int) {}"), + Example("@available ↓(*, deprecated)"), + Example("@MyPropertyWrapper ↓(param: 2) "), + Example("nonisolated ↓(unsafe) var _value: X?"), + Example("@MyProperty ↓() class Foo {}"), + Example(""" + let closure1 = { @MainActor ↓(a, b) in + } + """), + ], + corrections: [ + Example("private↓ (set) var foo: Bool = false"): Example("private(set) var foo: Bool = false"), + Example("fileprivate↓ (set) var foo: Bool = false"): Example("fileprivate(set) var foo: Bool = false"), + Example("internal↓ (set) var foo: Bool = false"): Example("internal(set) var foo: Bool = false"), + Example("public↓ (set) var foo: Bool = false"): Example("public(set) var foo: Bool = false"), + Example("public↓ (set) var foo: Bool = false"): Example("public(set) var foo: Bool = false"), + Example("@↓ MainActor"): Example("@MainActor"), + Example("func test(_ x: @↓ escaping () -> Int) {}"): Example("func test(_ x: @escaping () -> Int) {}"), + Example("func test(_ x: @escaping↓() -> Int) {}"): Example("func test(_ x: @escaping () -> Int) {}"), + Example("@available↓ (*, deprecated)"): Example("@available(*, deprecated)"), + Example("@MyPropertyWrapper↓ (param: 2) "): Example("@MyPropertyWrapper(param: 2) "), + Example("nonisolated↓ (unsafe) var _value: X?"): Example("nonisolated(unsafe) var _value: X?"), + Example("@MyProperty↓ ()"): Example("@MyProperty()"), + Example(""" + let closure1 = { @MainActor↓ (a, b) in + } + """): Example(""" + let closure1 = { @MainActor(a, b) in + } + """), + ] + ) +} + +private extension AttributeNameSpacingRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: DeclModifierSyntax) { + guard node.detail != nil, node.name.trailingTrivia.isNotEmpty else { + return + } + + addViolation( + startPosition: node.name.endPositionBeforeTrailingTrivia, + endPosition: node.name.endPosition, + replacement: "", + reason: "There must not be any space between access control modifier and scope" + ) + } + + override func visitPost(_ node: AttributeSyntax) { + // Check for trailing trivia after the '@' sign. Handles cases like `@ MainActor` / `@ escaping`. + if node.atSign.trailingTrivia.isNotEmpty { + addViolation( + startPosition: node.atSign.endPositionBeforeTrailingTrivia, + endPosition: node.atSign.endPosition, + replacement: "", + reason: "Attributes must not have trivia between `@` and the identifier" + ) + } + + let hasTrailingTrivia = node.attributeName.trailingTrivia.isNotEmpty + + // Handles cases like `@MyPropertyWrapper (param: 2)`. + if node.arguments != nil, hasTrailingTrivia { + addViolation( + startPosition: node.attributeName.endPositionBeforeTrailingTrivia, + endPosition: node.attributeName.endPosition, + replacement: "", + reason: "Attribute declarations with arguments must not have trailing trivia" + ) + } + + if !hasTrailingTrivia, node.isEscaping { + // Handles cases where escaping has the wrong spacing: `@escaping()` + addViolation( + startPosition: node.attributeName.endPositionBeforeTrailingTrivia, + endPosition: node.attributeName.endPosition, + replacement: " ", + reason: "`@escaping` must have a trailing space before the associated type" + ) + } + } + + private func addViolation( + startPosition: AbsolutePosition, + endPosition: AbsolutePosition, + replacement: String, + reason: String + ) { + let correction = ReasonedRuleViolation.ViolationCorrection( + start: startPosition, + end: endPosition, + replacement: replacement + ) + + let violation = ReasonedRuleViolation( + position: endPosition, + reason: reason, + severity: configuration.severity, + correction: correction + ) + violations.append(violation) + } + } +} + +private extension AttributeSyntax { + var isEscaping: Bool { + attributeNameText == "escaping" + } +} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 332102c95f..d00101a90b 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -31,6 +31,12 @@ final class ArrayInitRuleGeneratedTests: SwiftLintTestCase { } } +final class AttributeNameSpacingRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(AttributeNameSpacingRule.description) + } +} + final class AttributesRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(AttributesRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 88e14517c1..57148068b8 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -6,6 +6,8 @@ anonymous_argument_in_multiline_closure: severity: warning array_init: severity: warning +attribute_name_spacing: + severity: error attributes: severity: warning attributes_with_arguments_always_on_line_above: true From 27cab449acc9a7f4cb83f9ee854077a291a8d1bf Mon Sep 17 00:00:00 2001 From: Enric Enrich Date: Sat, 7 Sep 2024 14:49:42 +0200 Subject: [PATCH 024/260] Use `Diagnostics.error` when command failed (#5782) --- .../SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index 3ff555170e..4fdcb6bbca 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -32,6 +32,7 @@ struct SwiftLintCommandPlugin: CommandPlugin { try process.run() process.waitUntilExit() + switch process.terminationReason { case .exit: Diagnostics.remark("Finished running in module '\(target.name)'") @@ -40,9 +41,12 @@ struct SwiftLintCommandPlugin: CommandPlugin { @unknown default: Diagnostics.error("Stopped running in module '\(target.name) due to unexpected termination reason") } + if process.terminationStatus != EXIT_SUCCESS { - Diagnostics.warning( - "Command found violations or unsuccessfully stopped running in module '\(target.name)'" + Diagnostics.error(""" + Command found error violations or unsuccessfully stopped running with \ + exit code \(process.terminationStatus) in module '\(target.name)' + """ ) } } From e8451638a0b2a4ab21adbda875aef7306182bfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Sep 2024 16:59:58 +0200 Subject: [PATCH 025/260] Remove empty lines --- .github/workflows/docker.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1d996c4639..fc37c4292d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -10,36 +10,29 @@ on: jobs: build: runs-on: ubuntu-20.04 - steps: - uses: actions/checkout@v4 - - name: Extract DOCKER_TAG using tag name if: startsWith(github.ref, 'refs/tags/') run: | echo "DOCKER_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - - name: Use default DOCKER_TAG if: startsWith(github.ref, 'refs/tags/') != true run: | echo "DOCKER_TAG=latest" >> $GITHUB_ENV - - name: Set lowercase repository name run: | echo "REPOSITORY_LC=${REPOSITORY,,}" >>${GITHUB_ENV} env: REPOSITORY: '${{ github.repository }}' - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to GitHub registry - uses: docker/login-action@v3 + uses: docker/login-action@v3 with: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io - - uses: docker/build-push-action@v6 with: push: true From d601c22a173e9f7ab81d9d0d00a2506badb4db27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Sep 2024 18:02:57 +0200 Subject: [PATCH 026/260] Auto-upload Linux build --- .github/workflows/docker-release.yml | 21 +++++++++++++++++++ .github/workflows/docker.yml | 30 +++++++++++++++------------- Releasing.md | 1 - 3 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/docker-release.yml diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 0000000000..981a5ad407 --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,21 @@ +name: Docker Release + +on: + release: + types: [released] + +jobs: + trigger-build: + uses: ./.github/workflows/docker.yml + with: + tag: ${{ github.event.release.tag_name }} + secrets: inherit + upload-docker: + needs: trigger-build + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + - name: Upload binary to existing release + run: make zip_linux_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fc37c4292d..062924bbeb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,28 +1,30 @@ -name: docker +name: Docker Build on: push: branches: - main - tags: - - '*' + workflow_call: + inputs: + tag: + description: 'Docker tag' + required: true + type: string + default: 'latest' jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - - name: Extract DOCKER_TAG using tag name - if: startsWith(github.ref, 'refs/tags/') - run: | - echo "DOCKER_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - - name: Use default DOCKER_TAG - if: startsWith(github.ref, 'refs/tags/') != true - run: | - echo "DOCKER_TAG=latest" >> $GITHUB_ENV + - name: Set Docker tag + if: github.event_name == 'workflow_call' + run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV + - name: Use default Docker tag + if: github.event_name == 'push' + run: echo "DOCKER_TAG=latest" >> $GITHUB_ENV - name: Set lowercase repository name - run: | - echo "REPOSITORY_LC=${REPOSITORY,,}" >>${GITHUB_ENV} + run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> $GITHUB_ENV env: REPOSITORY: '${{ github.repository }}' - name: Set up Docker Buildx @@ -31,7 +33,7 @@ jobs: uses: docker/login-action@v3 with: username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io - uses: docker/build-push-action@v6 with: diff --git a/Releasing.md b/Releasing.md index fe7520ad4b..93aa81683e 100644 --- a/Releasing.md +++ b/Releasing.md @@ -10,5 +10,4 @@ For SwiftLint contributors, follow these steps to cut a release: 1. Make sure you have the latest stable Xcode version installed and `xcode-select`ed 1. Release new version: `make release "0.2.0: Tumble Dry"` -1. Wait for the Docker CI job to finish then run: `make zip_linux_release` 1. Celebrate. :tada: From d136b02f218146ca43cfdcdec3f6df00ee56f0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Sep 2024 18:03:15 +0200 Subject: [PATCH 027/260] Update release instructions --- Releasing.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Releasing.md b/Releasing.md index 93aa81683e..010f1ed2bf 100644 --- a/Releasing.md +++ b/Releasing.md @@ -7,7 +7,8 @@ For SwiftLint contributors, follow these steps to cut a release: * FabricSoftenerRule * Top Loading * Fresh Out Of The Dryer -1. Make sure you have the latest stable Xcode version installed and - `xcode-select`ed -1. Release new version: `make release "0.2.0: Tumble Dry"` +1. Make sure you have the latest stable Xcode version installed and `xcode-select`ed. +1. Make sure that the selected Xcode has the latest SDKs of all supported platforms installed. This is required to + build the CocoaPods release. +1. Release a new version by running `make release "0.2.0: Tumble Dry"`. 1. Celebrate. :tada: From 1dbe0957c6bdf49358d335819507913a121b2c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Sep 2024 18:08:02 +0200 Subject: [PATCH 028/260] Move release instructions --- CONTRIBUTING.md | 15 +++++++++++++++ Releasing.md | 14 -------------- 2 files changed, 15 insertions(+), 14 deletions(-) delete mode 100644 Releasing.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a9f305a99..51d771aaa8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -158,6 +158,21 @@ We follow the same syntax as CocoaPods' CHANGELOG.md: you may instead link to the change's pull request. 1. All CHANGELOG.md content is hard-wrapped at 80 characters. +## Cutting a Release + +SwiftLint maintainers follow these steps to cut a release: + +1. Come up with a witty washer- or dryer-themed release name. Past names include: + * Tumble Dry + * FabricSoftenerRule + * Top Loading + * Fresh Out Of The Dryer +1. Make sure you have the latest stable Xcode version installed and `xcode-select`ed. +1. Make sure that the selected Xcode has the latest SDKs of all supported platforms installed. This is required to + build the CocoaPods release. +1. Release a new version by running `make release "0.2.0: Tumble Dry"`. +1. Celebrate. :tada: + ## CI SwiftLint uses Azure Pipelines for most of its CI jobs, primarily because diff --git a/Releasing.md b/Releasing.md deleted file mode 100644 index 010f1ed2bf..0000000000 --- a/Releasing.md +++ /dev/null @@ -1,14 +0,0 @@ -# Releasing SwiftLint - -For SwiftLint contributors, follow these steps to cut a release: - -1. Come up with a witty washer- or dryer-themed release name. Past names include: - * Tumble Dry - * FabricSoftenerRule - * Top Loading - * Fresh Out Of The Dryer -1. Make sure you have the latest stable Xcode version installed and `xcode-select`ed. -1. Make sure that the selected Xcode has the latest SDKs of all supported platforms installed. This is required to - build the CocoaPods release. -1. Release a new version by running `make release "0.2.0: Tumble Dry"`. -1. Celebrate. :tada: From 06e4e3cc0779f04154bc790ca5ce5c0e340e6a3e Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Sat, 7 Sep 2024 22:15:21 +0100 Subject: [PATCH 029/260] Fix `superfluous_disable_command` for `custom_rules` (#5670) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 7 +- .../Extensions/Configuration+RulesMode.swift | 3 +- .../Configuration+RulesWrapper.swift | 5 +- Source/SwiftLintCore/Models/Linter.swift | 77 +++-- Source/SwiftLintCore/Models/Region.swift | 4 + Source/SwiftLintCore/Protocols/Rule.swift | 34 ++ Source/SwiftLintCore/Rules/CustomRules.swift | 34 +- .../Rules/SuperfluousDisableCommandRule.swift | 4 +- .../CustomRulesTests.swift | 319 +++++++++++++++--- 9 files changed, 402 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d7db8b24..3dd8e064e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,12 @@ #### Bug Fixes -* None. +* `superfluous_disable_command` violations are now triggered for + custom rules. + [Marcelo Fabri](https://github.com/marcelofabri) + [Martin Redington](https://github.com/mildm8nnered) + [SimplyDanny](https://github.com/SimplyDanny) + [#4754](https://github.com/realm/SwiftLint/issues/4754) ## 0.56.2: Heat Pump Dryer diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift index f7d0e6b7ad..e03cadd0a2 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift @@ -112,8 +112,7 @@ public extension Configuration { switch self { case let .only(onlyRules) where onlyRules.contains { $0 == CustomRules.description.identifier }: let customRulesRule = (allRulesWrapped.first { $0.rule is CustomRules })?.rule as? CustomRules - let customRuleIdentifiers = customRulesRule?.configuration.customRuleConfigurations.map(\.identifier) - return .only(onlyRules.union(Set(customRuleIdentifiers ?? []))) + return .only(onlyRules.union(Set(customRulesRule?.customRuleIdentifiers ?? []))) default: return self diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift index 18cdfc42b6..e06a692a6f 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift @@ -13,8 +13,7 @@ internal extension Configuration { private var validRuleIdentifiers: Set { let regularRuleIdentifiers = allRulesWrapped.map { type(of: $0.rule).description.identifier } let configurationCustomRulesIdentifiers = - (allRulesWrapped.first { $0.rule is CustomRules }?.rule as? CustomRules)? - .configuration.customRuleConfigurations.map(\.identifier) ?? [] + (allRulesWrapped.first { $0.rule is CustomRules }?.rule as? CustomRules)?.customRuleIdentifiers ?? [] return Set(regularRuleIdentifiers + configurationCustomRulesIdentifiers) } @@ -247,7 +246,7 @@ internal extension Configuration { as? CustomRules { onlyRules = onlyRules.union( Set( - childCustomRulesRule.configuration.customRuleConfigurations.map(\.identifier) + childCustomRulesRule.customRuleIdentifiers ) ) } diff --git a/Source/SwiftLintCore/Models/Linter.swift b/Source/SwiftLintCore/Models/Linter.swift index c18ce0ad1a..23bfad7d54 100644 --- a/Source/SwiftLintCore/Models/Linter.swift +++ b/Source/SwiftLintCore/Models/Linter.swift @@ -16,47 +16,56 @@ private struct LintResult { } private extension Rule { - static func superfluousDisableCommandViolations(regions: [Region], - superfluousDisableCommandRule: SuperfluousDisableCommandRule?, - allViolations: [StyleViolation]) -> [StyleViolation] { + func superfluousDisableCommandViolations(regions: [Region], + superfluousDisableCommandRule: SuperfluousDisableCommandRule?, + allViolations: [StyleViolation]) -> [StyleViolation] { guard regions.isNotEmpty, let superfluousDisableCommandRule else { return [] } - let regionsDisablingCurrentRule = regions.filter { region in - region.isRuleDisabled(self.init()) - } let regionsDisablingSuperfluousDisableRule = regions.filter { region in region.isRuleDisabled(superfluousDisableCommandRule) } - return regionsDisablingCurrentRule.compactMap { region -> StyleViolation? in - let isSuperfluousRuleDisabled = regionsDisablingSuperfluousDisableRule.contains { - $0.contains(region.start) - } - - guard !isSuperfluousRuleDisabled else { - return nil + var superfluousDisableCommandViolations = [StyleViolation]() + for region in regions { + if regionsDisablingSuperfluousDisableRule.contains(where: { $0.contains(region.start) }) { + continue } - - let noViolationsInDisabledRegion = !allViolations.contains { violation in - region.contains(violation.location) + let sortedDisabledIdentifiers = region.disabledRuleIdentifiers.sorted { + $0.stringRepresentation < $1.stringRepresentation } - guard noViolationsInDisabledRegion else { - return nil + commandIDsLoop: for disabledIdentifier in sortedDisabledIdentifiers { + guard !isEnabled(in: region, for: disabledIdentifier.stringRepresentation) else { + continue + } + var disableCommandValid = false + for violation in allViolations where region.contains(violation.location) { + if canBeDisabled(violation: violation, by: disabledIdentifier) { + disableCommandValid = true + continue commandIDsLoop + } + } + if !disableCommandValid { + let reason = superfluousDisableCommandRule.reason( + forRuleIdentifier: disabledIdentifier.stringRepresentation + ) + superfluousDisableCommandViolations.append( + StyleViolation( + ruleDescription: type(of: superfluousDisableCommandRule).description, + severity: superfluousDisableCommandRule.configuration.severity, + location: region.start, + reason: reason + ) + ) + } } - - return StyleViolation( - ruleDescription: type(of: superfluousDisableCommandRule).description, - severity: superfluousDisableCommandRule.configuration.severity, - location: region.start, - reason: superfluousDisableCommandRule.reason(for: self) - ) } + return superfluousDisableCommandViolations } // As we need the configuration to get custom identifiers. - // swiftlint:disable:next function_parameter_count + // swiftlint:disable:next function_parameter_count function_body_length func lint(file: SwiftLintFile, regions: [Region], benchmark: Bool, @@ -93,16 +102,26 @@ private extension Rule { let (disabledViolationsAndRegions, enabledViolationsAndRegions) = violations.map { violation in (violation, regions.first { $0.contains(violation.location) }) - }.partitioned { _, region in - region?.isRuleEnabled(self) ?? true + }.partitioned { violation, region in + if let region { + return isEnabled(in: region, for: violation.ruleIdentifier) + } + return true } + let customRulesIDs: [String] = { + guard let customRules = self as? CustomRules else { + return [] + } + return customRules.customRuleIdentifiers + }() let ruleIDs = Self.description.allIdentifiers + + customRulesIDs + (superfluousDisableCommandRule.map({ type(of: $0) })?.description.allIdentifiers ?? []) + [RuleIdentifier.all.stringRepresentation] let ruleIdentifiers = Set(ruleIDs.map { RuleIdentifier($0) }) - let superfluousDisableCommandViolations = Self.superfluousDisableCommandViolations( + let superfluousDisableCommandViolations = superfluousDisableCommandViolations( regions: regions.count > 1 ? file.regions(restrictingRuleIdentifiers: ruleIdentifiers) : regions, superfluousDisableCommandRule: superfluousDisableCommandRule, allViolations: violations diff --git a/Source/SwiftLintCore/Models/Region.swift b/Source/SwiftLintCore/Models/Region.swift index ae2df0fcba..9c6483fbd9 100644 --- a/Source/SwiftLintCore/Models/Region.swift +++ b/Source/SwiftLintCore/Models/Region.swift @@ -44,6 +44,10 @@ public struct Region: Equatable { /// /// - parameter rule: The rule whose status should be determined. /// + /// - note: For CustomRules, this will only return true if the `custom_rules` identifier + /// is used with the `swiftlint` disable command, but this method is never + /// called for CustomRules. + /// /// - returns: True if the specified rule is disabled in this region. public func isRuleDisabled(_ rule: some Rule) -> Bool { areRulesDisabled(ruleIDs: type(of: rule).description.allIdentifiers) diff --git a/Source/SwiftLintCore/Protocols/Rule.swift b/Source/SwiftLintCore/Protocols/Rule.swift index d04f234e53..5e0214b678 100644 --- a/Source/SwiftLintCore/Protocols/Rule.swift +++ b/Source/SwiftLintCore/Protocols/Rule.swift @@ -70,6 +70,26 @@ public protocol Rule { /// /// - returns: All style violations to the rule's expectations. func validate(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [StyleViolation] + + /// Checks if a style violation can be disabled by a command specifying a rule ID. Only the rule can claim that for + /// sure since it knows all the possible identifiers. + /// + /// - Parameters: + /// - violation: A style violation. + /// - ruleID: The name of a rule as used in a disable command. + /// + /// - Returns: A boolean value indicating whether the violation can be disabled by the given ID. + func canBeDisabled(violation: StyleViolation, by ruleID: RuleIdentifier) -> Bool + + /// Checks if a the rule is enabled in a given region. A specific rule ID can be provided in case a rule supports + /// more than one identifier. + /// + /// - Parameters: + /// - region: The region to check. + /// - ruleID: Rule identifier deviating from the default rule's name. + /// + /// - Returns: A boolean value indicating whether the rule is enabled in the given region. + func isEnabled(in region: Region, for ruleID: String) -> Bool } public extension Rule { @@ -110,6 +130,20 @@ public extension Rule { func createConfigurationDescription(exclusiveOptions: Set = []) -> RuleConfigurationDescription { RuleConfigurationDescription.from(configuration: configuration, exclusiveOptions: exclusiveOptions) } + + func canBeDisabled(violation: StyleViolation, by ruleID: RuleIdentifier) -> Bool { + switch ruleID { + case .all: + true + case let .single(identifier: id): + Self.description.allIdentifiers.contains(id) + && Self.description.allIdentifiers.contains(violation.ruleIdentifier) + } + } + + func isEnabled(in region: Region, for ruleID: String) -> Bool { + !Self.description.allIdentifiers.contains(ruleID) || region.isRuleEnabled(self) + } } public extension Rule { diff --git a/Source/SwiftLintCore/Rules/CustomRules.swift b/Source/SwiftLintCore/Rules/CustomRules.swift index c43e191f5e..e523c757a7 100644 --- a/Source/SwiftLintCore/Rules/CustomRules.swift +++ b/Source/SwiftLintCore/Rules/CustomRules.swift @@ -41,6 +41,10 @@ struct CustomRules: Rule, CacheDescriptionProvider { configuration.cacheDescription } + var customRuleIdentifiers: [String] { + configuration.customRuleConfigurations.map(\.identifier) + } + static let description = RuleDescription( identifier: "custom_rules", name: "Custom Rules", @@ -79,19 +83,29 @@ struct CustomRules: Rule, CacheDescriptionProvider { severity: configuration.severity, location: Location(file: file, characterOffset: $0.location), reason: configuration.message) - }).filter { violation in - guard let region = file.regions().first(where: { $0.contains(violation.location) }) else { - return true - } + }) + } + } - return !region.isRuleDisabled(customRuleIdentifier: configuration.identifier) - } + func canBeDisabled(violation: StyleViolation, by ruleID: RuleIdentifier) -> Bool { + switch ruleID { + case let .single(identifier: id): + id == Self.description.identifier + ? customRuleIdentifiers.contains(violation.ruleIdentifier) + : customRuleIdentifiers.contains(id) && violation.ruleIdentifier == id + default: + (self as any Rule).canBeDisabled(violation: violation, by: ruleID) } } -} -private extension Region { - func isRuleDisabled(customRuleIdentifier: String) -> Bool { - disabledRuleIdentifiers.contains(RuleIdentifier(customRuleIdentifier)) + func isEnabled(in region: Region, for ruleID: String) -> Bool { + if !Self.description.allIdentifiers.contains(ruleID), + !customRuleIdentifiers.contains(ruleID), + Self.description.identifier != ruleID { + return true + } + return !region.disabledRuleIdentifiers.contains(RuleIdentifier(Self.description.identifier)) + && !region.disabledRuleIdentifiers.contains(RuleIdentifier(ruleID)) + && !region.disabledRuleIdentifiers.contains(.all) } } diff --git a/Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift b/Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift index 0551575d87..fab3ef0b5a 100644 --- a/Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift +++ b/Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift @@ -34,9 +34,9 @@ package struct SuperfluousDisableCommandRule: SourceKitFreeRule { [] } - func reason(for rule: (some Rule).Type) -> String { + func reason(forRuleIdentifier ruleIdentifier: String) -> String { """ - SwiftLint rule '\(rule.description.identifier)' did not trigger a violation in the disabled region; \ + SwiftLint rule '\(ruleIdentifier)' did not trigger a violation in the disabled region; \ remove the disable command """ } diff --git a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift index 448886f60f..3337822047 100644 --- a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift @@ -2,8 +2,13 @@ import SourceKittenFramework @testable import SwiftLintCore import XCTest +// swiftlint:disable file_length +// swiftlint:disable:next type_body_length final class CustomRulesTests: SwiftLintTestCase { - typealias Configuration = RegexConfiguration + private typealias Configuration = RegexConfiguration + + private var testFile: SwiftLintFile { SwiftLintFile(path: "\(testResourcesPath)/test.txt")! } + func testCustomRuleConfigurationSetsCorrectlyWithMatchKinds() { let configDict = [ "my_custom_rule": [ @@ -122,10 +127,16 @@ final class CustomRulesTests: SwiftLintTestCase { ) } - func testLocalDisableCustomRule() { - let (_, customRules) = getCustomRules() - let file = SwiftLintFile(contents: "//swiftlint:disable custom \n// file with a pattern") - XCTAssertEqual(customRules.validate(file: file), []) + func testLocalDisableCustomRule() throws { + let customRules: [String: Any] = [ + "custom": [ + "regex": "pattern", + "match_kinds": "comment", + ], + ] + let example = Example("//swiftlint:disable custom \n// file with a pattern") + let violations = try violations(forExample: example, customRules: customRules) + XCTAssertTrue(violations.isEmpty) } func testLocalDisableCustomRuleWithMultipleRules() { @@ -147,7 +158,7 @@ final class CustomRulesTests: SwiftLintTestCase { func testCustomRulesIncludedDefault() { // Violation detected when included is omitted. let (_, customRules) = getCustomRules() - let violations = customRules.validate(file: getTestTextFile()) + let violations = customRules.validate(file: testFile) XCTAssertEqual(violations.count, 1) } @@ -158,8 +169,8 @@ final class CustomRulesTests: SwiftLintTestCase { customRuleConfiguration.customRuleConfigurations = [regexConfig] customRules.configuration = customRuleConfiguration - let violations = customRules.validate(file: getTestTextFile()) - XCTAssertEqual(violations.count, 0) + let violations = customRules.validate(file: testFile) + XCTAssertTrue(violations.isEmpty) } func testCustomRulesExcludedExcludesFile() { @@ -169,8 +180,8 @@ final class CustomRulesTests: SwiftLintTestCase { customRuleConfiguration.customRuleConfigurations = [regexConfig] customRules.configuration = customRuleConfiguration - let violations = customRules.validate(file: getTestTextFile()) - XCTAssertEqual(violations.count, 0) + let violations = customRules.validate(file: testFile) + XCTAssertTrue(violations.isEmpty) } func testCustomRulesExcludedArrayExcludesFile() { @@ -180,8 +191,8 @@ final class CustomRulesTests: SwiftLintTestCase { customRuleConfiguration.customRuleConfigurations = [regexConfig] customRules.configuration = customRuleConfiguration - let violations = customRules.validate(file: getTestTextFile()) - XCTAssertEqual(violations.count, 0) + let violations = customRules.validate(file: testFile) + XCTAssertTrue(violations.isEmpty) } func testCustomRulesCaptureGroup() { @@ -189,12 +200,235 @@ final class CustomRulesTests: SwiftLintTestCase { "regex": #"\ba\s+(\w+)"#, "capture_group": 1, ]) - let violations = customRules.validate(file: getTestTextFile()) + let violations = customRules.validate(file: testFile) XCTAssertEqual(violations.count, 1) XCTAssertEqual(violations[0].location.line, 2) XCTAssertEqual(violations[0].location.character, 6) } + // MARK: - superfluous_disable_command support + + func testCustomRulesTriggersSuperfluousDisableCommand() throws { + let customRuleIdentifier = "forbidden" + let customRules: [String: Any] = [ + customRuleIdentifier: [ + "regex": "FORBIDDEN", + ], + ] + let example = Example(""" + // swiftlint:disable:next custom_rules + let ALLOWED = 2 + """) + + let violations = try violations(forExample: example, customRules: customRules) + XCTAssertEqual(violations.count, 1) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "custom_rules")) + } + + func testSpecificCustomRuleTriggersSuperfluousDisableCommand() throws { + let customRuleIdentifier = "forbidden" + let customRules: [String: Any] = [ + customRuleIdentifier: [ + "regex": "FORBIDDEN", + ], + ] + + let example = Example(""" + // swiftlint:disable:next \(customRuleIdentifier) + let ALLOWED = 2 + """) + + let violations = try violations(forExample: example, customRules: customRules) + XCTAssertEqual(violations.count, 1) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: customRuleIdentifier)) + } + + func testSpecificAndCustomRulesTriggersSuperfluousDisableCommand() throws { + let customRuleIdentifier = "forbidden" + let customRules: [String: Any] = [ + customRuleIdentifier: [ + "regex": "FORBIDDEN", + ], + ] + + let example = Example(""" + // swiftlint:disable:next custom_rules \(customRuleIdentifier) + let ALLOWED = 2 + """) + + let violations = try violations(forExample: example, customRules: customRules) + + XCTAssertEqual(violations.count, 2) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "custom_rules")) + XCTAssertTrue(violations[1].isSuperfluousDisableCommandViolation(for: "\(customRuleIdentifier)")) + } + + func testCustomRulesViolationAndViolationOfSuperfluousDisableCommand() throws { + let customRuleIdentifier = "forbidden" + let customRules: [String: Any] = [ + customRuleIdentifier: [ + "regex": "FORBIDDEN", + ], + ] + + let example = Example(""" + let FORBIDDEN = 1 + // swiftlint:disable:next \(customRuleIdentifier) + let ALLOWED = 2 + """) + + let violations = try violations(forExample: example, customRules: customRules) + + XCTAssertEqual(violations.count, 2) + XCTAssertEqual(violations[0].ruleIdentifier, customRuleIdentifier) + XCTAssertTrue(violations[1].isSuperfluousDisableCommandViolation(for: customRuleIdentifier)) + } + + func testDisablingCustomRulesDoesNotTriggerSuperfluousDisableCommand() throws { + let customRules: [String: Any] = [ + "forbidden": [ + "regex": "FORBIDDEN", + ], + ] + + let example = Example(""" + // swiftlint:disable:next custom_rules + let FORBIDDEN = 1 + """) + + XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) + } + + func testMultipleSpecificCustomRulesTriggersSuperfluousDisableCommand() throws { + let customRules = [ + "forbidden": [ + "regex": "FORBIDDEN", + ], + "forbidden2": [ + "regex": "FORBIDDEN2", + ], + ] + let example = Example(""" + // swiftlint:disable:next forbidden forbidden2 + let ALLOWED = 2 + """) + + let violations = try self.violations(forExample: example, customRules: customRules) + XCTAssertEqual(violations.count, 2) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "forbidden")) + XCTAssertTrue(violations[1].isSuperfluousDisableCommandViolation(for: "forbidden2")) + } + + func testUnviolatedSpecificCustomRulesTriggersSuperfluousDisableCommand() throws { + let customRules = [ + "forbidden": [ + "regex": "FORBIDDEN", + ], + "forbidden2": [ + "regex": "FORBIDDEN2", + ], + ] + let example = Example(""" + // swiftlint:disable:next forbidden forbidden2 + let FORBIDDEN = 1 + """) + + let violations = try self.violations(forExample: example, customRules: customRules) + XCTAssertEqual(violations.count, 1) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "forbidden2")) + } + + func testViolatedSpecificAndGeneralCustomRulesTriggersSuperfluousDisableCommand() throws { + let customRules = [ + "forbidden": [ + "regex": "FORBIDDEN", + ], + "forbidden2": [ + "regex": "FORBIDDEN2", + ], + ] + let example = Example(""" + // swiftlint:disable:next forbidden forbidden2 custom_rules + let FORBIDDEN = 1 + """) + + let violations = try self.violations(forExample: example, customRules: customRules) + XCTAssertEqual(violations.count, 1) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "forbidden2")) + } + + func testSuperfluousDisableCommandWithMultipleCustomRules() throws { + let customRules: [String: Any] = [ + "custom1": [ + "regex": "pattern", + "match_kinds": "comment", + ], + "custom2": [ + "regex": "10", + "match_kinds": "number", + ], + "custom3": [ + "regex": "100", + "match_kinds": "number", + ], + ] + + let example = Example( + """ + // swiftlint:disable custom1 custom3 + return 10 + """ + ) + + let violations = try violations(forExample: example, customRules: customRules) + + XCTAssertEqual(violations.count, 3) + XCTAssertEqual(violations[0].ruleIdentifier, "custom2") + XCTAssertTrue(violations[1].isSuperfluousDisableCommandViolation(for: "custom1")) + XCTAssertTrue(violations[2].isSuperfluousDisableCommandViolation(for: "custom3")) + } + + func testViolatedCustomRuleDoesNotTriggerSuperfluousDisableCommand() throws { + let customRules: [String: Any] = [ + "dont_print": [ + "regex": "print\\(" + ], + ] + let example = Example(""" + // swiftlint:disable:next dont_print + print("Hello, world") + """) + XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) + } + + func testDisableAllDoesNotTriggerSuperfluousDisableCommand() throws { + let customRules: [String: Any] = [ + "dont_print": [ + "regex": "print\\(" + ], + ] + let example = Example(""" + // swiftlint:disable:next all + print("Hello, world") + """) + XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) + } + + func testDisableAllAndDisableSpecificCustomRuleDoesNotTriggerSuperfluousDisableCommand() throws { + let customRules: [String: Any] = [ + "dont_print": [ + "regex": "print\\(" + ], + ] + let example = Example(""" + // swiftlint:disable:next all dont_print + print("Hello, world") + """) + XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) + } + + // MARK: - Private + private func getCustomRules(_ extraConfig: [String: Any] = [:]) -> (Configuration, CustomRules) { var config: [String: Any] = [ "regex": "pattern", @@ -202,18 +436,8 @@ final class CustomRulesTests: SwiftLintTestCase { ] extraConfig.forEach { config[$0] = $1 } - var regexConfig = RegexConfiguration(identifier: "custom") - do { - try regexConfig.apply(configuration: config) - } catch { - XCTFail("Failed regex config") - } - - var customRuleConfiguration = CustomRulesConfiguration() - customRuleConfiguration.customRuleConfigurations = [regexConfig] - - var customRules = CustomRules() - customRules.configuration = customRuleConfiguration + let regexConfig = configuration(withIdentifier: "custom", configurationDict: config) + let customRules = customRules(withConfigurations: [regexConfig]) return (regexConfig, customRules) } @@ -223,34 +447,53 @@ final class CustomRulesTests: SwiftLintTestCase { "match_kinds": "comment", ] - var regexConfig1 = Configuration(identifier: "custom1") - do { - try regexConfig1.apply(configuration: config1) - } catch { - XCTFail("Failed regex config") - } + let regexConfig1 = configuration(withIdentifier: "custom1", configurationDict: config1) let config2 = [ "regex": "something", "match_kinds": "comment", ] - var regexConfig2 = Configuration(identifier: "custom2") + let regexConfig2 = configuration(withIdentifier: "custom2", configurationDict: config2) + + let customRules = customRules(withConfigurations: [regexConfig1, regexConfig2]) + return ((regexConfig1, regexConfig2), customRules) + } + + private func violations(forExample example: Example, customRules: [String: Any]) throws -> [StyleViolation] { + let configDict: [String: Any] = [ + "only_rules": ["custom_rules", "superfluous_disable_command"], + "custom_rules": customRules, + ] + let configuration = try SwiftLintCore.Configuration(dict: configDict) + return SwiftLintTestHelpers.violations( + example.skipWrappingInCommentTest(), + config: configuration + ) + } + + private func configuration(withIdentifier identifier: String, configurationDict: [String: Any]) -> Configuration { + var regexConfig = Configuration(identifier: identifier) do { - try regexConfig2.apply(configuration: config2) + try regexConfig.apply(configuration: configurationDict) } catch { XCTFail("Failed regex config") } + return regexConfig + } + private func customRules(withConfigurations configurations: [Configuration]) -> CustomRules { var customRuleConfiguration = CustomRulesConfiguration() - customRuleConfiguration.customRuleConfigurations = [regexConfig1, regexConfig2] - + customRuleConfiguration.customRuleConfigurations = configurations var customRules = CustomRules() customRules.configuration = customRuleConfiguration - return ((regexConfig1, regexConfig2), customRules) + return customRules } +} - private func getTestTextFile() -> SwiftLintFile { - SwiftLintFile(path: "\(testResourcesPath)/test.txt")! +private extension StyleViolation { + func isSuperfluousDisableCommandViolation(for ruleIdentifier: String) -> Bool { + self.ruleIdentifier == SuperfluousDisableCommandRule.description.identifier && + reason.contains("SwiftLint rule '\(ruleIdentifier)' did not trigger a violation") } } From 9e7805478104cfc3b99c02280549bdb7e01a85c9 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Sun, 8 Sep 2024 12:31:45 +0100 Subject: [PATCH 030/260] Preserve trailing comments for `opening_brace` rule (#5780) --- CHANGELOG.md | 5 +++++ .../Rules/Style/OpeningBraceRule.swift | 5 +++-- .../Style/OpeningBraceRuleExamples.swift | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd8e064e8..a27ac00fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#4754](https://github.com/realm/SwiftLint/issues/4754) +* Trailing comments are now preserved by the `opening_brace` rule when + rewriting. + [Martin Redington](https://github.com/mildm8nnered) + [#5751](https://github.com/realm/SwiftLint/issues/5751) + ## 0.56.2: Heat Pump Dryer #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift index 4e3e46b864..fe5b55f517 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift @@ -172,10 +172,11 @@ private extension OpeningBraceRule { let previousLocation = previousToken.endLocation(converter: locationConverter) let leftBraceLocation = leftBrace.startLocation(converter: locationConverter) if previousLocation.line != leftBraceLocation.line { + let trailingCommentText = previousToken.trailingTrivia.description.trimmingCharacters(in: .whitespaces) return .init( start: previousToken.endPositionBeforeTrailingTrivia, - end: openingPosition, - replacement: " " + end: openingPosition.advanced(by: trailingCommentText.isNotEmpty ? 1 : 0), + replacement: trailingCommentText.isNotEmpty ? " { \(trailingCommentText)" : " " ) } if previousLocation.column + 1 == leftBraceLocation.column { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRuleExamples.swift index e41dc09977..d423c231f2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRuleExamples.swift @@ -547,6 +547,28 @@ struct OpeningBraceRuleExamples { } """), // https://github.com/realm/SwiftLint/issues/5598 + Example(""" + if c // A comment + { + return + } + """): Example(""" + if c { // A comment + return + } + """), + // https://github.com/realm/SwiftLint/issues/5751 + Example(""" + if c // A comment + { // Another comment + return + } + """): Example(""" + if c { // A comment // Another comment + return + } + """), + // https://github.com/realm/SwiftLint/issues/5751 Example(""" func foo() { if q1, q2 From 8baec9eed80a191b66ac683863f7ff8a593b23f3 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Sun, 8 Sep 2024 14:22:58 +0100 Subject: [PATCH 031/260] no_magic_numbers rule now ignores violations in Preview macros (#5778) --- CHANGELOG.md | 5 +++++ .../Rules/Idiomatic/NoMagicNumbersRule.swift | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a27ac00fec..af653bdb3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,11 @@ [Sam Rayner](https://github.com/samrayner) [#5263](https://github.com/realm/SwiftLint/issues/5263) +* The `no_magic_numbers` rule will now ignore violations in + SwiftUI's `Preview` macro. + [Martin Redington](https://github.com/mildm8nnered) + [#5778](https://github.com/realm/SwiftLint/issues/5778) + #### Bug Fixes * `superfluous_disable_command` violations are now triggered for diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift index 3af3d6e77a..85cad443b6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift @@ -81,6 +81,11 @@ struct NoMagicNumbersRule: OptInRule { Example("let (lowerBound, upperBound) = (400, 599)"), Example("let a = (5, 10)"), Example("let notFound = (statusCode: 404, description: \"Not Found\", isError: true)"), + Example(""" + #Preview { + ContentView(value: 5) + } + """), ], triggeringExamples: [ Example("foo(↓321)"), @@ -107,6 +112,11 @@ struct NoMagicNumbersRule: OptInRule { """), Example("let imageHeight = (width - ↓24)"), Example("return (↓5, ↓10, ↓15)"), + Example(""" + #ExampleMacro { + ContentView(value: ↓5) + } + """), ] ) } @@ -121,6 +131,10 @@ private extension NoMagicNumbersRule { node.isSimpleTupleAssignment ? .skipChildren : .visitChildren } + override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind { + node.macroName.text == "Preview" ? .skipChildren : .visitChildren + } + override func visitPost(_ node: ClassDeclSyntax) { let className = node.name.text if node.isXCTestCase(configuration.testParentClasses) { From 7afe2dc1e7dd5d1a7a63b2acff65a35a3d7794ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 14:53:13 +0200 Subject: [PATCH 032/260] Combine release jobs into one workflow --- .github/workflows/docker-release.yml | 21 ------------------- .../{plugins-release.yml => release.yml} | 18 ++++++++++++++-- 2 files changed, 16 insertions(+), 23 deletions(-) delete mode 100644 .github/workflows/docker-release.yml rename .github/workflows/{plugins-release.yml => release.yml} (64%) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml deleted file mode 100644 index 981a5ad407..0000000000 --- a/.github/workflows/docker-release.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Docker Release - -on: - release: - types: [released] - -jobs: - trigger-build: - uses: ./.github/workflows/docker.yml - with: - tag: ${{ github.event.release.tag_name }} - secrets: inherit - upload-docker: - needs: trigger-build - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Upload binary to existing release - run: make zip_linux_release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/plugins-release.yml b/.github/workflows/release.yml similarity index 64% rename from .github/workflows/plugins-release.yml rename to .github/workflows/release.yml index fd2f6b5704..847357b4a3 100644 --- a/.github/workflows/plugins-release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,11 @@ -name: Plugins Release +name: Release on: release: types: [released] jobs: - dispatch: + dispatch-plugins: runs-on: ubuntu-latest steps: - name: Checkout repository @@ -25,3 +25,17 @@ jobs: "tag": "${{ github.ref_name }}", "checksum": "${{ steps.parse_checksum.outputs.checksum }}" } + trigger-docker: + uses: ./.github/workflows/docker.yml + with: + tag: ${{ github.event.release.tag_name }} + secrets: inherit + upload-docker: + needs: trigger-docker + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + - name: Upload binary to existing release + run: make zip_linux_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 1db3efde3df08aae1c40af534b0a9e68b3864b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 15:03:54 +0200 Subject: [PATCH 033/260] Get tag name from release object --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 847357b4a3..42d85270a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: client-payload: |- { "title": "${{ github.event.release.name }}", - "tag": "${{ github.ref_name }}", + "tag": "${{ github.event.release.tag_name }}", "checksum": "${{ steps.parse_checksum.outputs.checksum }}" } trigger-docker: From 551adb047f9b9da784e910e974946ce278b28532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 15:58:22 +0200 Subject: [PATCH 034/260] Automate Pod publishing --- .github/workflows/release.yml | 13 +++++++++++++ Makefile | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42d85270a1..ed786cc887 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,3 +39,16 @@ jobs: run: make zip_linux_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + publish-pod: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 + - name: Retrieve author in uppercase + id: retrieve_author + run: | + AUTHOR=${{ github.event.release.author.login }} + echo "name=${AUTHOR@U}" >> $GITHUB_OUTPUT + - name: Deploy to CocoaPods + run: make pod_publish + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} diff --git a/Makefile b/Makefile index 2dcae7143c..effd9eb355 100644 --- a/Makefile +++ b/Makefile @@ -153,8 +153,10 @@ docker_htop: display_compilation_time: $(BUILD_TOOL) $(XCODEFLAGS) OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" clean build-for-testing | grep -E ^[1-9]{1}[0-9]*.[0-9]+ms | sort -n -publish: +formula_bump: brew update && brew bump-formula-pr --tag=$(shell git describe --tags) --revision=$(shell git rev-parse HEAD) swiftlint + +pod_publish: bundle install bundle exec pod trunk push SwiftLint.podspec @@ -186,7 +188,7 @@ endif git push origin HEAD git push origin $(NEW_VERSION) ./tools/create-github-release.sh "$(NEW_VERSION)" - make publish + make formula_bump ./tools/add-new-changelog-section.sh git commit -a -m "Add new changelog section" git push origin HEAD From 168fb98ed1f3e343d703ecceaf518b6cf565207b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 18:02:27 +0200 Subject: [PATCH 035/260] Release 0.57.0 --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Package.swift | 4 ++-- Source/SwiftLintCore/Models/Version.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af653bdb3b..fbff6bbc06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Main +## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index 0ce621122c..f905115cda 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.56.2", + version = "0.57.0", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Package.swift b/Package.swift index 22207c767e..27d7d33a2b 100644 --- a/Package.swift +++ b/Package.swift @@ -172,8 +172,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.56.2/SwiftLintBinary-macos.artifactbundle.zip", - checksum: "197df93d7f5041d8cd46d6902a34ad57914efe1b5b50635995f3b9065f2c3ffd" + url: "https://github.com/realm/SwiftLint/releases/download/0.57.0/SwiftLintBinary-macos.artifactbundle.zip", + checksum: "a1bbafe57538077f3abe4cfb004b0464dcd87e8c23611a2153c675574b858b3a" ) ) #endif diff --git a/Source/SwiftLintCore/Models/Version.swift b/Source/SwiftLintCore/Models/Version.swift index 60cbe4057f..d5a8f6e690 100644 --- a/Source/SwiftLintCore/Models/Version.swift +++ b/Source/SwiftLintCore/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable { } /// The current SwiftLint version. - public static let current = Self(value: "0.56.2") + public static let current = Self(value: "0.57.0") /// Public initializer. /// From 06547973b849314ea4a079677a9e303567d1f3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 18:02:57 +0200 Subject: [PATCH 036/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbff6bbc06..c962702a1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## Main + +#### Breaking + +* None. + +#### Experimental + +* None. + +#### Enhancements + +* None. + +#### Bug Fixes + +* None. + ## 0.57.0: Squeaky Clean Cycle #### Breaking From 9da392d1583443fc68365e6f54f86cc787b43810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 18:09:19 +0200 Subject: [PATCH 037/260] Fix workflow syntax --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed786cc887..51345f51b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,9 +27,9 @@ jobs: } trigger-docker: uses: ./.github/workflows/docker.yml + secrets: inherit with: tag: ${{ github.event.release.tag_name }} - secrets: inherit upload-docker: needs: trigger-docker runs-on: ubuntu-20.04 From 614e694928332716a7c659260d2c4a70005e3ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 18:19:39 +0200 Subject: [PATCH 038/260] Allow to trigger Docker build manually --- .github/workflows/docker.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 062924bbeb..fcb133e479 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,6 +11,13 @@ on: required: true type: string default: 'latest' + workflow_dispatch: + inputs: + tag: + description: 'Docker tag' + required: true + type: string + default: 'latest' jobs: build: From 274d12b1a06a4d5b2a27143be724eff01142496c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 8 Sep 2024 18:21:28 +0200 Subject: [PATCH 039/260] Read tag from workflow input --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fcb133e479..ed6825e433 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Docker tag - if: github.event_name == 'workflow_call' + if: github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - name: Use default Docker tag if: github.event_name == 'push' From 85c4dbe963ab42875fd4d89307b628aeae073ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 9 Sep 2024 23:18:00 +0200 Subject: [PATCH 040/260] Ensure expected initializer signature (#5786) --- .../Rules/Lint/OptionalDataStringConversionRule.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift index a98b6dddf9..337d68b207 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift @@ -9,7 +9,10 @@ struct OptionalDataStringConversionRule: Rule { description: "Prefer failable `String(data:encoding:)` initializer when converting `Data` to `String`", kind: .lint, nonTriggeringExamples: [ - Example("String(data: data, encoding: .utf8)") + Example("String(data: data, encoding: .utf8)"), + Example("String(UTF8.self)"), + Example("String(a, b, c, UTF8.self)"), + Example("String(decoding: data, encoding: UTF8.self)"), ], triggeringExamples: [ Example("String(decoding: data, as: UTF8.self)") @@ -22,6 +25,7 @@ private extension OptionalDataStringConversionRule { override func visitPost(_ node: DeclReferenceExprSyntax) { if node.baseName.text == "String", let parent = node.parent?.as(FunctionCallExprSyntax.self), + parent.arguments.map(\.label?.text) == ["decoding", "as"], let expr = parent.arguments.last?.expression.as(MemberAccessExprSyntax.self), expr.base?.description == "UTF8", expr.declName.baseName.description == "self" { From 8241909addfd1a10cefde7d0619c4b9c16ba43b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 11 Sep 2024 23:16:39 +0200 Subject: [PATCH 041/260] Run command plugin in whole package if no targets defined (#5789) --- CHANGELOG.md | 5 +- .../SwiftLintCommandPlugin.swift | 64 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c962702a1f..43b2f1aeb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,10 @@ #### Bug Fixes -* None. +* Run command plugin in whole package if no targets are defined in the + package manifest. + [SimplyDanny](https://github.com/SimplyDanny) + [#5787](https://github.com/realm/SwiftLint/issues/5787) ## 0.57.0: Squeaky Clean Cycle diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index 4fdcb6bbca..d254ff8fdd 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -13,42 +13,52 @@ struct SwiftLintCommandPlugin: CommandPlugin { let targets = targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames) - let tool = try context.tool(named: "swiftlint") + guard !targets.isEmpty else { + try run(with: context, arguments: arguments) + return + } for target in targets { guard let target = target.sourceModule else { Diagnostics.warning("Target '\(target.name)' is not a source module; skipping it") continue } + try run(in: target.directory.string, for: target.name, with: context, arguments: arguments) + } + } - let process = Process() - process.currentDirectoryURL = URL(fileURLWithPath: context.package.directory.string) - process.executableURL = URL(fileURLWithPath: tool.path.string) - process.arguments = arguments - if !arguments.contains("analyze") { - // The analyze command does not support the `--cache-path` argument. - process.arguments! += ["--cache-path", "\(context.pluginWorkDirectory.string)"] - } - process.arguments! += [target.directory.string] + private func run(in directory: String = ".", + for targetName: String? = nil, + with context: PluginContext, + arguments: [String]) throws { + let process = Process() + process.currentDirectoryURL = URL(fileURLWithPath: context.package.directory.string) + process.executableURL = URL(fileURLWithPath: try context.tool(named: "swiftlint").path.string) + process.arguments = arguments + if !arguments.contains("analyze") { + // The analyze command does not support the `--cache-path` argument. + process.arguments! += ["--cache-path", "\(context.pluginWorkDirectory.string)"] + } + process.arguments! += [directory] - try process.run() - process.waitUntilExit() + try process.run() + process.waitUntilExit() - switch process.terminationReason { - case .exit: - Diagnostics.remark("Finished running in module '\(target.name)'") - case .uncaughtSignal: - Diagnostics.error("Got uncaught signal while running in module '\(target.name)'") - @unknown default: - Diagnostics.error("Stopped running in module '\(target.name) due to unexpected termination reason") - } + let module = targetName.map { "module '\($0)'" } ?? "package" + switch process.terminationReason { + case .exit: + Diagnostics.remark("Finished running in \(module)") + case .uncaughtSignal: + Diagnostics.error("Got uncaught signal while running in \(module)") + @unknown default: + Diagnostics.error("Stopped running in \(module) due to unexpected termination reason") + } - if process.terminationStatus != EXIT_SUCCESS { - Diagnostics.error(""" - Command found error violations or unsuccessfully stopped running with \ - exit code \(process.terminationStatus) in module '\(target.name)' - """ - ) - } + if process.terminationStatus != EXIT_SUCCESS { + Diagnostics.error(""" + Command found error violations or unsuccessfully stopped running with \ + exit code \(process.terminationStatus) in \(module) + """ + ) } } } From 0d04196f92605735821630165bc64c3c3229c913 Mon Sep 17 00:00:00 2001 From: Vladimir Burdukov Date: Sun, 15 Sep 2024 22:13:31 +0300 Subject: [PATCH 042/260] Do not throw deprecation warning if property is not present in configuration (#5792) --- CHANGELOG.md | 5 ++++ .../RuleConfigurationMacros.swift | 4 ++- Tests/MacroTests/AutoConfigParserTests.swift | 26 +++++++++++++------ .../RuleConfigurationDescriptionTests.swift | 11 +++++--- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43b2f1aeb0..8349e5b825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5787](https://github.com/realm/SwiftLint/issues/5787) +* Do not throw deprecation warning if deprecated property is not + presented in configuration. + [chipp](https://github.com/chipp) + [#5791](https://github.com/realm/SwiftLint/issues/5791) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift index 665a9e4335..e1a44256e8 100644 --- a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift +++ b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift @@ -74,7 +74,9 @@ enum AutoConfigParser: MemberMacro { """ for option in nonInlinedOptions { """ - try \(raw: option).apply(configuration[$\(raw: option).key], ruleID: Parent.identifier) + if let value = configuration[$\(raw: option).key] { + try \(raw: option).apply(value, ruleID: Parent.identifier) + } """ } """ diff --git a/Tests/MacroTests/AutoConfigParserTests.swift b/Tests/MacroTests/AutoConfigParserTests.swift index 39d9505032..4164eb411f 100644 --- a/Tests/MacroTests/AutoConfigParserTests.swift +++ b/Tests/MacroTests/AutoConfigParserTests.swift @@ -80,8 +80,12 @@ final class AutoConfigParserTests: XCTestCase { guard let configuration = configuration as? [String: Any] else { throw Issue.invalidConfiguration(ruleID: Parent.identifier) } - try eA.apply(configuration[$eA.key], ruleID: Parent.identifier) - try eB.apply(configuration[$eB.key], ruleID: Parent.identifier) + if let value = configuration[$eA.key] { + try eA.apply(value, ruleID: Parent.identifier) + } + if let value = configuration[$eB.key] { + try eB.apply(value, ruleID: Parent.identifier) + } if !supportedKeys.isSuperset(of: configuration.keys) { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() @@ -131,8 +135,12 @@ final class AutoConfigParserTests: XCTestCase { guard let configuration = configuration as? [String: Any] else { return } - try eA.apply(configuration[$eA.key], ruleID: Parent.identifier) - try eC.apply(configuration[$eC.key], ruleID: Parent.identifier) + if let value = configuration[$eA.key] { + try eA.apply(value, ruleID: Parent.identifier) + } + if let value = configuration[$eC.key] { + try eC.apply(value, ruleID: Parent.identifier) + } if !supportedKeys.isSuperset(of: configuration.keys) { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() @@ -177,7 +185,6 @@ final class AutoConfigParserTests: XCTestCase { } func testSeverityAppliedTwice() { - // swiftlint:disable line_length assertMacroExpansion( """ @AutoConfigParser @@ -211,8 +218,12 @@ final class AutoConfigParserTests: XCTestCase { guard let configuration = configuration as? [String: Any] else { return } - try severityConfiguration.apply(configuration[$severityConfiguration.key], ruleID: Parent.identifier) - try foo.apply(configuration[$foo.key], ruleID: Parent.identifier) + if let value = configuration[$severityConfiguration.key] { + try severityConfiguration.apply(value, ruleID: Parent.identifier) + } + if let value = configuration[$foo.key] { + try foo.apply(value, ruleID: Parent.identifier) + } if !supportedKeys.isSuperset(of: configuration.keys) { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() @@ -222,6 +233,5 @@ final class AutoConfigParserTests: XCTestCase { """, macros: macros ) - // swiftlint:enable line_length } } diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift b/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift index a4fc6bd707..a656666d60 100644 --- a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift +++ b/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift @@ -499,6 +499,12 @@ final class RuleConfigurationDescriptionTests: XCTestCase { ) } + func testNoDeprecationWarningIfNoDeprecatedPropertySet() throws { + var configuration = TestConfiguration() + + XCTAssert(try Issue.captureConsole { try configuration.apply(configuration: ["flag": false]) }.isEmpty) + } + func testInvalidKeys() throws { var configuration = TestConfiguration() @@ -511,10 +517,7 @@ final class RuleConfigurationDescriptionTests: XCTestCase { "unsupported": true, ]) }, - """ - warning: Configuration option 'set' in 'my_rule' rule is deprecated. Use the option 'other_opt' instead. - warning: Configuration for 'RuleMock' rule contains the invalid key(s) 'unknown', 'unsupported'. - """ + "warning: Configuration for 'RuleMock' rule contains the invalid key(s) 'unknown', 'unsupported'." ) } From a826d177c56aa8538438435e4d8288691653d249 Mon Sep 17 00:00:00 2001 From: minhaaan Date: Mon, 16 Sep 2024 04:14:06 +0900 Subject: [PATCH 043/260] Use SwiftSyntax version 600.0.0 (#5795) --- MODULE.bazel | 2 +- Package.resolved | 4 ++-- Package.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index f905115cda..8ec7b30033 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -11,7 +11,7 @@ bazel_dep(name = "platforms", version = "0.0.10") bazel_dep(name = "rules_apple", version = "3.8.0", repo_name = "build_bazel_rules_apple") bazel_dep(name = "rules_swift", version = "2.1.1", repo_name = "build_bazel_rules_swift") bazel_dep(name = "sourcekitten", version = "0.36.0", repo_name = "com_github_jpsim_sourcekitten") -bazel_dep(name = "swift-syntax", version = "600.0.0-prerelease-2024-08-14", repo_name = "SwiftSyntax") +bazel_dep(name = "swift-syntax", version = "600.0.0", repo_name = "SwiftSyntax") bazel_dep(name = "swift_argument_parser", version = "1.3.1.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser") bazel_dep(name = "yams", version = "5.1.3", repo_name = "sourcekitten_com_github_jpsim_yams") diff --git a/Package.resolved b/Package.resolved index 204129e258..ee63d9748b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { - "revision" : "515f79b522918f83483068d99c68daeb5116342d", - "version" : "600.0.0-prerelease-2024-08-14" + "revision" : "cb53fa1bd3219b0b23ded7dfdd3b2baff266fd25", + "version" : "600.0.0" } }, { diff --git a/Package.swift b/Package.swift index 27d7d33a2b..3b24604dfd 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.1"), - .package(url: "https://github.com/swiftlang/swift-syntax.git", exact: "600.0.0-prerelease-2024-08-14"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", exact: "600.0.0"), .package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.35.0")), .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), From 37f15d4388e0897fbb7507dbb82cd20285826472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 16 Sep 2024 20:16:35 +0200 Subject: [PATCH 044/260] Suggest broader initializer accepting Sequences (#5794) --- CHANGELOG.md | 6 +++++- .../Rules/Lint/OptionalDataStringConversionRule.swift | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8349e5b825..7257d37b63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,11 @@ #### Enhancements -* None. +* Suggest failable `String(bytes:encoding:)` initializer in + `optional_data_string_conversion` rule as it accepts all `Sequence` + types. + [Jordan Rose](https://github.com/jrose-signal) + [SimplyDanny](https://github.com/SimplyDanny) #### Bug Fixes diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift index 337d68b207..1aa44801ec 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/OptionalDataStringConversionRule.swift @@ -3,13 +3,15 @@ import SwiftSyntax @SwiftSyntaxRule struct OptionalDataStringConversionRule: Rule { var configuration = SeverityConfiguration(.warning) + static let description = RuleDescription( identifier: "optional_data_string_conversion", name: "Optional Data -> String Conversion", - description: "Prefer failable `String(data:encoding:)` initializer when converting `Data` to `String`", + description: "Prefer failable `String(bytes:encoding:)` initializer when converting `Data` to `String`", kind: .lint, nonTriggeringExamples: [ Example("String(data: data, encoding: .utf8)"), + Example("String(bytes: data, encoding: .utf8)"), Example("String(UTF8.self)"), Example("String(a, b, c, UTF8.self)"), Example("String(decoding: data, encoding: UTF8.self)"), From e9386aea51e56c398eb0809ad5993f5a78d13e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 16 Sep 2024 21:49:15 +0200 Subject: [PATCH 045/260] Disallow optional configuration values (#5798) Can be avoided now given 0d04196. --- .../Models/RuleConfigurationDescription.swift | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift index b09b4a914e..b0e5750897 100644 --- a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift +++ b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift @@ -336,8 +336,10 @@ public protocol AcceptableByConfigurationElement { /// Update the object. /// - /// - Parameter value: New underlying data for the object. - mutating func apply(_ value: Any?, ruleID: String) throws + /// - Parameters: + /// - value: New underlying data for the object. + /// - ruleID: The rule's identifier in which context the configuration parsing runs. + mutating func apply(_ value: Any, ruleID: String) throws } /// Default implementations which are shortcuts applicable for most of the types conforming to the protocol. @@ -346,10 +348,8 @@ public extension AcceptableByConfigurationElement { RuleConfigurationDescription(options: [key => asOption()]) } - mutating func apply(_ value: Any?, ruleID: String) throws { - if let value { - self = try Self(fromAny: value, context: ruleID) - } + mutating func apply(_ value: Any, ruleID: String) throws { + self = try Self(fromAny: value, context: ruleID) } } @@ -657,10 +657,8 @@ public extension AcceptableByConfigurationElement where Self: RuleConfiguration return RuleConfigurationDescription(options: [key => asOption()]) } - mutating func apply(_ value: Any?, ruleID _: String) throws { - if let value { - try apply(configuration: value) - } + mutating func apply(_ value: Any, ruleID _: String) throws { + try apply(configuration: value) } init(fromAny _: Any, context _: String) throws { From 1767dab4858a310719fdf44e6c8dd9dc371503f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 21 Sep 2024 13:53:30 +0200 Subject: [PATCH 046/260] Add Swift 6 presentation of `map(_:)` type (#5804) --- .../Rules/Lint/TypesafeArrayInitRule.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift index 0d7ea85937..faa619e479 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift @@ -51,10 +51,16 @@ struct TypesafeArrayInitRule: AnalyzerRule { ) private static let parentRule = ArrayInitRule() - private static let mapTypePattern = regex(""" - \\Q \ - \\Q(Self) -> ((Self.Element) throws -> T) throws -> [T]\\E - """) + private static let mapTypePatterns = [ + regex(""" + \\Q \ + \\Q(Self) -> ((Self.Element) throws -> T) throws -> [T]\\E + """), + regex(""" + \\Q (Self) -> ((Self.Element) throws(E) -> T) throws(E) -> [T]\\E + """), + ] func validate(file: SwiftLintFile, compilerArguments: [String]) -> [StyleViolation] { guard let filePath = file.path else { @@ -83,7 +89,9 @@ struct TypesafeArrayInitRule: AnalyzerRule { if let isSystem = pointee["key.is_system"], isSystem.isEqualTo(true), let name = pointee["key.name"], name.isEqualTo("map(_:)"), let typeName = pointee["key.typename"] as? String { - return Self.mapTypePattern.numberOfMatches(in: typeName, range: typeName.fullNSRange) == 1 + return Self.mapTypePatterns.contains { + $0.numberOfMatches(in: typeName, range: typeName.fullNSRange) == 1 + } } return false } From 246643fe55f2b024804551016b0ed9394e056684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 17:23:46 +0200 Subject: [PATCH 047/260] Keep only command-line related functions in `swiftlint` module (#5806) Swift 6 doesn't allow importing executable targets. So we need to move testable logic into a framework. --- .bazelrc | 1 - BUILD | 18 +-- Package.swift | 2 +- .../Benchmark.swift | 1 - .../CompilerArgumentsExtractor.swift | 0 .../Configuration+CommandLine.swift | 1 - .../ExitHelper.swift | 4 +- Source/SwiftLintFramework/Exports.swift | 2 +- .../LintOrAnalyzeCommand.swift | 139 ++++++++++++------ .../LintableFilesVisitor.swift | 1 - .../ProcessInfo+XcodeCloud.swift | 0 .../ProgressBar.swift | 1 - .../RulesFilter.swift | 20 +-- .../Signposts.swift | 0 .../SwiftLintError.swift | 4 +- .../SwiftPMCompilationDB.swift | 0 .../UpdateChecker.swift | 5 +- ....ExcludingOptions+RulesFilterOptions.swift | 20 --- Source/swiftlint/Commands/GenerateDocs.swift | 2 +- Source/swiftlint/Commands/Rules.swift | 2 +- .../LintOrAnalyzeArguments.swift | 1 + .../Common/RulesFilterOptions.swift | 20 +++ Tests/BUILD | 2 +- Tests/CLITests/RulesFilterTests.swift | 1 - 24 files changed, 144 insertions(+), 103 deletions(-) rename Source/{swiftlint/Helpers => SwiftLintFramework}/Benchmark.swift (98%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/CompilerArgumentsExtractor.swift (100%) rename Source/{swiftlint/Extensions => SwiftLintFramework}/Configuration+CommandLine.swift (99%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/ExitHelper.swift (68%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/LintOrAnalyzeCommand.swift (86%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/LintableFilesVisitor.swift (99%) rename Source/{swiftlint/Extensions => SwiftLintFramework}/ProcessInfo+XcodeCloud.swift (100%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/ProgressBar.swift (98%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/RulesFilter.swift (63%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/Signposts.swift (100%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/SwiftLintError.swift (66%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/SwiftPMCompilationDB.swift (100%) rename Source/{swiftlint/Helpers => SwiftLintFramework}/UpdateChecker.swift (95%) delete mode 100644 Source/swiftlint/Commands/Common/RulesFilter.ExcludingOptions+RulesFilterOptions.swift rename Source/swiftlint/{Helpers => Common}/LintOrAnalyzeArguments.swift (99%) rename Source/swiftlint/{Commands => }/Common/RulesFilterOptions.swift (51%) diff --git a/.bazelrc b/.bazelrc index aef68378fb..12ae6e95c6 100644 --- a/.bazelrc +++ b/.bazelrc @@ -13,7 +13,6 @@ build --disk_cache=~/.bazel_cache build --experimental_remote_cache_compression build --remote_build_event_upload=minimal build --nolegacy_important_outputs -build --swiftcopt=-warnings-as-errors build:release \ --compilation_mode=opt \ diff --git a/BUILD b/BUILD index b13f510f8e..9dd4eacfee 100644 --- a/BUILD +++ b/BUILD @@ -142,33 +142,23 @@ swift_library( ":SwiftLintBuiltInRules", ":SwiftLintCore", ":SwiftLintExtraRules", + "@com_github_johnsundell_collectionconcurrencykit//:CollectionConcurrencyKit", ], ) -swift_library( - name = "swiftlint.library", +swift_binary( + name = "swiftlint", package_name = "SwiftLint", srcs = glob(["Source/swiftlint/**/*.swift"]), - copts = copts, # TODO: strict_concurrency_copts - module_name = "swiftlint", + copts = copts, visibility = ["//visibility:public"], deps = [ ":SwiftLintFramework", - "@com_github_johnsundell_collectionconcurrencykit//:CollectionConcurrencyKit", "@sourcekitten_com_github_apple_swift_argument_parser//:ArgumentParser", "@swiftlint_com_github_scottrhoyt_swifty_text_table//:SwiftyTextTable", ], ) -swift_binary( - name = "swiftlint", - copts = copts + strict_concurrency_copts, - visibility = ["//visibility:public"], - deps = [ - ":swiftlint.library", - ], -) - apple_universal_binary( name = "universal_swiftlint", binary = ":swiftlint", diff --git a/Package.swift b/Package.swift index 3b24604dfd..0dfcc60a62 100644 --- a/Package.swift +++ b/Package.swift @@ -60,7 +60,7 @@ let package = Package( .testTarget( name: "CLITests", dependencies: [ - "swiftlint" + "SwiftLintFramework", ], swiftSettings: swiftFeatures ), diff --git a/Source/swiftlint/Helpers/Benchmark.swift b/Source/SwiftLintFramework/Benchmark.swift similarity index 98% rename from Source/swiftlint/Helpers/Benchmark.swift rename to Source/SwiftLintFramework/Benchmark.swift index b05755babb..76b113155c 100644 --- a/Source/swiftlint/Helpers/Benchmark.swift +++ b/Source/SwiftLintFramework/Benchmark.swift @@ -1,5 +1,4 @@ import Foundation -import SwiftLintFramework struct BenchmarkEntry { let id: String diff --git a/Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift b/Source/SwiftLintFramework/CompilerArgumentsExtractor.swift similarity index 100% rename from Source/swiftlint/Helpers/CompilerArgumentsExtractor.swift rename to Source/SwiftLintFramework/CompilerArgumentsExtractor.swift diff --git a/Source/swiftlint/Extensions/Configuration+CommandLine.swift b/Source/SwiftLintFramework/Configuration+CommandLine.swift similarity index 99% rename from Source/swiftlint/Extensions/Configuration+CommandLine.swift rename to Source/SwiftLintFramework/Configuration+CommandLine.swift index 6eb4eeb061..49e62b9a69 100644 --- a/Source/swiftlint/Extensions/Configuration+CommandLine.swift +++ b/Source/SwiftLintFramework/Configuration+CommandLine.swift @@ -1,7 +1,6 @@ import CollectionConcurrencyKit import Foundation import SourceKittenFramework -import SwiftLintFramework private actor CounterActor { private var count = 0 diff --git a/Source/swiftlint/Helpers/ExitHelper.swift b/Source/SwiftLintFramework/ExitHelper.swift similarity index 68% rename from Source/swiftlint/Helpers/ExitHelper.swift rename to Source/SwiftLintFramework/ExitHelper.swift index cd20c6a7de..f2bf1281ea 100644 --- a/Source/swiftlint/Helpers/ExitHelper.swift +++ b/Source/SwiftLintFramework/ExitHelper.swift @@ -2,8 +2,8 @@ import Glibc #endif -enum ExitHelper { - static func successfullyExit() { +package enum ExitHelper { + package static func successfullyExit() { #if os(Linux) // Workaround for https://github.com/apple/swift/issues/59961 Glibc.exit(0) diff --git a/Source/SwiftLintFramework/Exports.swift b/Source/SwiftLintFramework/Exports.swift index b61dabc36c..a352d954cd 100644 --- a/Source/SwiftLintFramework/Exports.swift +++ b/Source/SwiftLintFramework/Exports.swift @@ -6,7 +6,7 @@ private let _registerAllRulesOnceImpl: Void = { RuleRegistry.shared.register(rules: builtInRules + coreRules + extraRules()) }() -public extension RuleRegistry { +package extension RuleRegistry { /// Register all rules. Should only be called once before any SwiftLint code is executed. static func registerAllRulesOnce() { _ = _registerAllRulesOnceImpl diff --git a/Source/swiftlint/Helpers/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift similarity index 86% rename from Source/swiftlint/Helpers/LintOrAnalyzeCommand.swift rename to Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 1994c12ff4..b2d313233b 100644 --- a/Source/swiftlint/Helpers/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -1,11 +1,12 @@ import Dispatch import Foundation -import SwiftLintFramework -enum LintOrAnalyzeMode { +// swiftlint:disable file_length + +package enum LintOrAnalyzeMode { case lint, analyze - var imperative: String { + package var imperative: String { switch self { case .lint: return "lint" @@ -14,7 +15,7 @@ enum LintOrAnalyzeMode { } } - var verb: String { + package var verb: String { switch self { case .lint: return "linting" @@ -24,8 +25,98 @@ enum LintOrAnalyzeMode { } } -struct LintOrAnalyzeCommand { - static func run(_ options: LintOrAnalyzeOptions) async throws { +package struct LintOrAnalyzeOptions { + let mode: LintOrAnalyzeMode + let paths: [String] + let useSTDIN: Bool + let configurationFiles: [String] + let strict: Bool + let lenient: Bool + let forceExclude: Bool + let useExcludingByPrefix: Bool + let useScriptInputFiles: Bool + let benchmark: Bool + let reporter: String? + let baseline: String? + let writeBaseline: String? + let workingDirectory: String? + let quiet: Bool + let output: String? + let progress: Bool + let cachePath: String? + let ignoreCache: Bool + let enableAllRules: Bool + let onlyRule: String? + let autocorrect: Bool + let format: Bool + let compilerLogPath: String? + let compileCommands: String? + let checkForUpdates: Bool + + package init(mode: LintOrAnalyzeMode, + paths: [String], + useSTDIN: Bool, + configurationFiles: [String], + strict: Bool, + lenient: Bool, + forceExclude: Bool, + useExcludingByPrefix: Bool, + useScriptInputFiles: Bool, + benchmark: Bool, + reporter: String?, + baseline: String?, + writeBaseline: String?, + workingDirectory: String?, + quiet: Bool, + output: String?, + progress: Bool, + cachePath: String?, + ignoreCache: Bool, + enableAllRules: Bool, + onlyRule: String?, + autocorrect: Bool, + format: Bool, + compilerLogPath: String?, + compileCommands: String?, + checkForUpdates: Bool) { + self.mode = mode + self.paths = paths + self.useSTDIN = useSTDIN + self.configurationFiles = configurationFiles + self.strict = strict + self.lenient = lenient + self.forceExclude = forceExclude + self.useExcludingByPrefix = useExcludingByPrefix + self.useScriptInputFiles = useScriptInputFiles + self.benchmark = benchmark + self.reporter = reporter + self.baseline = baseline + self.writeBaseline = writeBaseline + self.workingDirectory = workingDirectory + self.quiet = quiet + self.output = output + self.progress = progress + self.cachePath = cachePath + self.ignoreCache = ignoreCache + self.enableAllRules = enableAllRules + self.onlyRule = onlyRule + self.autocorrect = autocorrect + self.format = format + self.compilerLogPath = compilerLogPath + self.compileCommands = compileCommands + self.checkForUpdates = checkForUpdates + } + + var verb: String { + if autocorrect { + return "correcting" + } + return mode.verb + } +} + +package struct LintOrAnalyzeCommand { + package static func run(_ options: LintOrAnalyzeOptions) async throws { if let workingDirectory = options.workingDirectory { if !FileManager.default.changeCurrentDirectoryPath(workingDirectory) { throw SwiftLintError.usageError( @@ -251,42 +342,6 @@ struct LintOrAnalyzeCommand { } } -struct LintOrAnalyzeOptions { - let mode: LintOrAnalyzeMode - let paths: [String] - let useSTDIN: Bool - let configurationFiles: [String] - let strict: Bool - let lenient: Bool - let forceExclude: Bool - let useExcludingByPrefix: Bool - let useScriptInputFiles: Bool - let benchmark: Bool - let reporter: String? - let baseline: String? - let writeBaseline: String? - let workingDirectory: String? - let quiet: Bool - let output: String? - let progress: Bool - let cachePath: String? - let ignoreCache: Bool - let enableAllRules: Bool - let onlyRule: String? - let autocorrect: Bool - let format: Bool - let compilerLogPath: String? - let compileCommands: String? - let checkForUpdates: Bool - - var verb: String { - if autocorrect { - return "correcting" - } - return mode.verb - } -} - private class LintOrAnalyzeResultBuilder { var fileBenchmark = Benchmark(name: "files") var ruleBenchmark = Benchmark(name: "rules") diff --git a/Source/swiftlint/Helpers/LintableFilesVisitor.swift b/Source/SwiftLintFramework/LintableFilesVisitor.swift similarity index 99% rename from Source/swiftlint/Helpers/LintableFilesVisitor.swift rename to Source/SwiftLintFramework/LintableFilesVisitor.swift index c200513478..97c5a2e535 100644 --- a/Source/swiftlint/Helpers/LintableFilesVisitor.swift +++ b/Source/SwiftLintFramework/LintableFilesVisitor.swift @@ -1,6 +1,5 @@ import Foundation import SourceKittenFramework -import SwiftLintFramework typealias File = String typealias Arguments = [String] diff --git a/Source/swiftlint/Extensions/ProcessInfo+XcodeCloud.swift b/Source/SwiftLintFramework/ProcessInfo+XcodeCloud.swift similarity index 100% rename from Source/swiftlint/Extensions/ProcessInfo+XcodeCloud.swift rename to Source/SwiftLintFramework/ProcessInfo+XcodeCloud.swift diff --git a/Source/swiftlint/Helpers/ProgressBar.swift b/Source/SwiftLintFramework/ProgressBar.swift similarity index 98% rename from Source/swiftlint/Helpers/ProgressBar.swift rename to Source/SwiftLintFramework/ProgressBar.swift index 0d50555df6..b5a08ab62f 100644 --- a/Source/swiftlint/Helpers/ProgressBar.swift +++ b/Source/SwiftLintFramework/ProgressBar.swift @@ -1,6 +1,5 @@ import Dispatch import Foundation -import SwiftLintFramework // Inspired by https://github.com/jkandzi/Progress.swift actor ProgressBar { diff --git a/Source/swiftlint/Helpers/RulesFilter.swift b/Source/SwiftLintFramework/RulesFilter.swift similarity index 63% rename from Source/swiftlint/Helpers/RulesFilter.swift rename to Source/SwiftLintFramework/RulesFilter.swift index ad56eb29b4..9f585f7be4 100644 --- a/Source/swiftlint/Helpers/RulesFilter.swift +++ b/Source/SwiftLintFramework/RulesFilter.swift @@ -1,23 +1,25 @@ -import SwiftLintFramework +package final class RulesFilter { + package struct ExcludingOptions: OptionSet { + package let rawValue: Int -final class RulesFilter { - struct ExcludingOptions: OptionSet { - let rawValue: Int + package init(rawValue: Int) { + self.rawValue = rawValue + } - static let enabled = Self(rawValue: 1 << 0) - static let disabled = Self(rawValue: 1 << 1) - static let uncorrectable = Self(rawValue: 1 << 2) + package static let enabled = Self(rawValue: 1 << 0) + package static let disabled = Self(rawValue: 1 << 1) + package static let uncorrectable = Self(rawValue: 1 << 2) } private let allRules: RuleList private let enabledRules: [any Rule] - init(allRules: RuleList = RuleRegistry.shared.list, enabledRules: [any Rule]) { + package init(allRules: RuleList = RuleRegistry.shared.list, enabledRules: [any Rule]) { self.allRules = allRules self.enabledRules = enabledRules } - func getRules(excluding excludingOptions: ExcludingOptions) -> RuleList { + package func getRules(excluding excludingOptions: ExcludingOptions) -> RuleList { if excludingOptions.isEmpty { return allRules } diff --git a/Source/swiftlint/Helpers/Signposts.swift b/Source/SwiftLintFramework/Signposts.swift similarity index 100% rename from Source/swiftlint/Helpers/Signposts.swift rename to Source/SwiftLintFramework/Signposts.swift diff --git a/Source/swiftlint/Helpers/SwiftLintError.swift b/Source/SwiftLintFramework/SwiftLintError.swift similarity index 66% rename from Source/swiftlint/Helpers/SwiftLintError.swift rename to Source/SwiftLintFramework/SwiftLintError.swift index cf17a123a4..da6ee8fd49 100644 --- a/Source/swiftlint/Helpers/SwiftLintError.swift +++ b/Source/SwiftLintFramework/SwiftLintError.swift @@ -1,9 +1,9 @@ import Foundation -enum SwiftLintError: LocalizedError { +package enum SwiftLintError: LocalizedError { case usageError(description: String) - var errorDescription: String? { + package var errorDescription: String? { switch self { case .usageError(let description): return description diff --git a/Source/swiftlint/Helpers/SwiftPMCompilationDB.swift b/Source/SwiftLintFramework/SwiftPMCompilationDB.swift similarity index 100% rename from Source/swiftlint/Helpers/SwiftPMCompilationDB.swift rename to Source/SwiftLintFramework/SwiftPMCompilationDB.swift diff --git a/Source/swiftlint/Helpers/UpdateChecker.swift b/Source/SwiftLintFramework/UpdateChecker.swift similarity index 95% rename from Source/swiftlint/Helpers/UpdateChecker.swift rename to Source/SwiftLintFramework/UpdateChecker.swift index 15f4294b85..81043ce36b 100644 --- a/Source/swiftlint/Helpers/UpdateChecker.swift +++ b/Source/SwiftLintFramework/UpdateChecker.swift @@ -14,10 +14,9 @@ import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endif -import SwiftLintFramework -enum UpdateChecker { - static func checkForUpdates() { +package enum UpdateChecker { + package static func checkForUpdates() { guard let url = URL(string: "https://api.github.com/repos/realm/SwiftLint/releases/latest"), let data = sendRequest(to: url), let latestVersionNumber = parseVersionNumber(data) else { diff --git a/Source/swiftlint/Commands/Common/RulesFilter.ExcludingOptions+RulesFilterOptions.swift b/Source/swiftlint/Commands/Common/RulesFilter.ExcludingOptions+RulesFilterOptions.swift deleted file mode 100644 index 192bf6895a..0000000000 --- a/Source/swiftlint/Commands/Common/RulesFilter.ExcludingOptions+RulesFilterOptions.swift +++ /dev/null @@ -1,20 +0,0 @@ -extension RulesFilter.ExcludingOptions { - static func excludingOptions(byCommandLineOptions rulesFilterOptions: RulesFilterOptions) -> Self { - var excludingOptions: Self = [] - - switch rulesFilterOptions.ruleEnablement { - case .enabled: - excludingOptions.insert(.disabled) - case .disabled: - excludingOptions.insert(.enabled) - case .none: - break - } - - if rulesFilterOptions.correctable { - excludingOptions.insert(.uncorrectable) - } - - return excludingOptions - } -} diff --git a/Source/swiftlint/Commands/GenerateDocs.swift b/Source/swiftlint/Commands/GenerateDocs.swift index fc448fc594..b62e6ae8f9 100644 --- a/Source/swiftlint/Commands/GenerateDocs.swift +++ b/Source/swiftlint/Commands/GenerateDocs.swift @@ -18,7 +18,7 @@ extension SwiftLint { func run() throws { let configuration = Configuration(configurationFiles: [config].compactMap({ $0 })) let rulesFilter = RulesFilter(enabledRules: configuration.rules) - let rules = rulesFilter.getRules(excluding: .excludingOptions(byCommandLineOptions: rulesFilterOptions)) + let rules = rulesFilter.getRules(excluding: rulesFilterOptions.excludingOptions) try RuleListDocumentation(rules) .write(to: URL(fileURLWithPath: path, isDirectory: true)) diff --git a/Source/swiftlint/Commands/Rules.swift b/Source/swiftlint/Commands/Rules.swift index 33cb9bd94a..0ed3a966a7 100644 --- a/Source/swiftlint/Commands/Rules.swift +++ b/Source/swiftlint/Commands/Rules.swift @@ -40,7 +40,7 @@ extension SwiftLint { return } let rules = RulesFilter(enabledRules: configuration.rules) - .getRules(excluding: .excludingOptions(byCommandLineOptions: rulesFilterOptions)) + .getRules(excluding: rulesFilterOptions.excludingOptions) .list .sorted { $0.0 < $1.0 } if configOnly { diff --git a/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift similarity index 99% rename from Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift rename to Source/swiftlint/Common/LintOrAnalyzeArguments.swift index 779293fdf7..8b9b00eba5 100644 --- a/Source/swiftlint/Helpers/LintOrAnalyzeArguments.swift +++ b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift @@ -1,4 +1,5 @@ import ArgumentParser +import SwiftLintFramework enum LeniencyOptions: String, EnumerableFlag { case strict, lenient diff --git a/Source/swiftlint/Commands/Common/RulesFilterOptions.swift b/Source/swiftlint/Common/RulesFilterOptions.swift similarity index 51% rename from Source/swiftlint/Commands/Common/RulesFilterOptions.swift rename to Source/swiftlint/Common/RulesFilterOptions.swift index e4bfe4fb47..456bfe44d5 100644 --- a/Source/swiftlint/Commands/Common/RulesFilterOptions.swift +++ b/Source/swiftlint/Common/RulesFilterOptions.swift @@ -1,4 +1,5 @@ import ArgumentParser +import SwiftLintFramework enum RuleEnablementOptions: String, EnumerableFlag { case enabled, disabled @@ -17,4 +18,23 @@ struct RulesFilterOptions: ParsableArguments { var ruleEnablement: RuleEnablementOptions? @Flag(name: .shortAndLong, help: "Only display correctable rules") var correctable = false + + var excludingOptions: RulesFilter.ExcludingOptions { + var excludingOptions: RulesFilter.ExcludingOptions = [] + + switch ruleEnablement { + case .enabled: + excludingOptions.insert(.disabled) + case .disabled: + excludingOptions.insert(.enabled) + case .none: + break + } + + if correctable { + excludingOptions.insert(.uncorrectable) + } + + return excludingOptions + } } diff --git a/Tests/BUILD b/Tests/BUILD index 509bce099d..01aa65395c 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -13,7 +13,7 @@ swift_library( module_name = "CLITests", package_name = "SwiftLint", deps = [ - "//:swiftlint.library", + "//:SwiftLintFramework", ], copts = copts, ) diff --git a/Tests/CLITests/RulesFilterTests.swift b/Tests/CLITests/RulesFilterTests.swift index e3546c0810..7c2f3fcfa4 100644 --- a/Tests/CLITests/RulesFilterTests.swift +++ b/Tests/CLITests/RulesFilterTests.swift @@ -1,4 +1,3 @@ -@testable import swiftlint import SwiftLintFramework import XCTest From 8db0fbf6e8ed33dc0b37bffff2e8a8122dbc65a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 19:18:02 +0200 Subject: [PATCH 048/260] Extend clean command --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index effd9eb355..b20fa4e15c 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,8 @@ analyze_autocorrect: write_xcodebuild_log clean: rm -f "$(OUTPUT_PACKAGE)" rm -rf "$(TEMPORARY_FOLDER)" - rm -f "./*.zip" "bazel.tar.gz" "bazel.tar.gz.sha256" + rm -rf rule_docs/ docs/ + rm -f ./*.{zip,pkg} bazel.tar.gz bazel.tar.gz.sha256 swift package clean clean_xcode: From 0c9ea0e74054df7a28148fdc30a696e0fef4d056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 19:36:26 +0200 Subject: [PATCH 049/260] Add Swift 6 builds (#5810) --- .../SwiftVersionTests.swift | 4 +- azure-pipelines.yml | 43 ++++++++----------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift index 179849adaa..fb103a7b7e 100644 --- a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift +++ b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift @@ -3,7 +3,9 @@ import XCTest final class SwiftVersionTests: SwiftLintTestCase { func testDetectSwiftVersion() { -#if compiler(>=6.0.0) +#if compiler(>=6.0.1) + let version = "6.0.1" +#elseif compiler(>=6.0.0) let version = "6.0.0" #elseif compiler(>=5.10.1) let version = "5.10.1" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 992185d397..f4e8aaa3d9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,47 +4,42 @@ trigger: jobs: - job: Linux pool: - vmImage: 'ubuntu-22.04' + vmImage: 'ubuntu-24.04' strategy: maxParallel: 10 matrix: - swift-5.10.1: - containerImage: swift:5.10.1 - container: $[ variables['containerImage'] ] + 'Swift 5.10.1': + image: swift:5.10.1-noble + 'Swift 6': + image: swift:6.0-noble + container: $[ variables['image'] ] steps: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test -- job: macOS13 - pool: - vmImage: 'macOS-13' +- job: macOS strategy: maxParallel: 10 + matrix: + '13': + image: 'macOS-13' + xcode: '15.2' + '14': + image: 'macOS-14' + xcode: '15.4' + pool: + vmImage: $(image) + variables: + DEVELOPER_DIR: /Applications/Xcode_$(xcode).app steps: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test -# TODO: Re-enable when FB11648454 is fixed -# - job: Xcode -# pool: -# vmImage: 'macOS-12' -# strategy: -# maxParallel: 10 -# matrix: -# xcode14: -# DEVELOPER_DIR: /Applications/Xcode_14.0.1.app -# steps: -# - script: | -# sw_vers -# xcodebuild -version -# displayName: Version Informations -# - script: xcodebuild -scheme swiftlint test -destination "platform=macOS" OTHER_SWIFT_FLAGS="\$(inherited) -D DISABLE_FOCUSED_EXAMPLES" -# displayName: xcodebuild test - job: CocoaPods pool: vmImage: 'macOS-14' variables: - DEVELOPER_DIR: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_16.app steps: - script: bundle install --path vendor/bundle displayName: bundle install From 36b88c5e88a513d644c66afc66cc409c9726601c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 20:15:31 +0200 Subject: [PATCH 050/260] Remove unused `testSimulateHomebrewTest` (#5812) This test has been inactive for the last 5 years and it doesn't run anymore. --- Tests/IntegrationTests/IntegrationTests.swift | 173 ------------------ 1 file changed, 173 deletions(-) diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index bbb8318ee9..fb6945ed47 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -69,64 +69,6 @@ final class IntegrationTests: SwiftLintTestCase { .appendingPathComponent("default_rule_configurations.yml") XCTAssertEqual(defaultConfig + "\n", try String(contentsOf: referenceFile)) } - - func testSimulateHomebrewTest() { - // Since this test uses the `swiftlint` binary built while building `SwiftLintPackageTests`, - // we run it only on macOS using SwiftPM. -#if os(macOS) && SWIFT_PACKAGE - let keyName = "SWIFTLINT_FRAMEWORK_TEST_ENABLE_SIMULATE_HOMEBREW_TEST" - guard ProcessInfo.processInfo.environment[keyName] != nil else { - print(""" - Skipping the opt-in test `\(#function)`. - Set the `\(keyName)` environment variable to test `\(#function)`. - """) - return - } - guard let swiftlintURL = swiftlintBuiltBySwiftPM(), - let (testSwiftURL, seatbeltURL) = prepareSandbox() else { - return - } - - defer { - try? FileManager.default.removeItem(at: testSwiftURL.deletingLastPathComponent()) - try? FileManager.default.removeItem(at: seatbeltURL) - } - - let swiftlintInSandboxArgs = [ - "sandbox-exec", "-f", seatbeltURL.path, "sh", "-c", - "SWIFTLINT_SWIFT_VERSION=5 \(swiftlintURL.path) --no-cache", - ] - let swiftlintResult = execute(swiftlintInSandboxArgs, in: testSwiftURL.deletingLastPathComponent()) - let statusWithoutCrash: Int32 = 0 - let stdoutWithoutCrash = """ - \(testSwiftURL.path):1:1: \ - warning: Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline) - - """ - let stderrWithoutCrash = """ - Linting Swift files at paths \n\ - Linting 'Test.swift' (1/1) - Connection invalid - Most rules will be skipped because sourcekitd has failed. - Done linting! Found 1 violation, 0 serious in 1 file. - - """ - if #available(macOS 10.14.1, *) { - // Within a sandbox on macOS 10.14.1+, `swiftlint` crashes with "Test::Unit::AssertionFailedError" - // error in `libxpc.dylib` when calling `sourcekitd_send_request_sync`. - // - // Since Homebrew CI succeeded in bottling swiftlint 0.27.0 on release of macOS 10.14, - // `swiftlint` may not crash on macOS 10.14. But that is not confirmed. - XCTAssertNotEqual(swiftlintResult.status, statusWithoutCrash, "It is expected to crash.") - XCTAssertNotEqual(swiftlintResult.stdout, stdoutWithoutCrash) - XCTAssertNotEqual(swiftlintResult.stderr, stderrWithoutCrash) - } else { - XCTAssertEqual(swiftlintResult.status, statusWithoutCrash) - XCTAssertEqual(swiftlintResult.stdout, stdoutWithoutCrash) - XCTAssertEqual(swiftlintResult.stderr, stderrWithoutCrash) - } -#endif - } } private struct StaticStringImitator { @@ -162,118 +104,3 @@ private extension String { StaticStringImitator(string: self).withStaticString(closure) } } - -#if os(macOS) && SWIFT_PACKAGE - -private func execute(_ args: [String], - in directory: URL? = nil, - input: Data? = nil) -> (status: Int32, stdout: String, stderr: String) { - let process = Process() - process.launchPath = "/usr/bin/env" - process.arguments = args - if let directory { - process.currentDirectoryPath = directory.path - } - let stdoutPipe = Pipe(), stderrPipe = Pipe() - process.standardOutput = stdoutPipe - process.standardError = stderrPipe - if let input { - let stdinPipe = Pipe() - process.standardInput = stdinPipe.fileHandleForReading - stdinPipe.fileHandleForWriting.write(input) - stdinPipe.fileHandleForWriting.closeFile() - } - let group = DispatchGroup(), queue = DispatchQueue.global() - var stdoutData: Data?, stderrData: Data? - process.launch() - queue.async(group: group) { stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile() } - queue.async(group: group) { stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile() } - process.waitUntilExit() - group.wait() - let stdout = stdoutData.flatMap { String(data: $0, encoding: .utf8) } ?? "" - let stderr = stderrData.flatMap { String(data: $0, encoding: .utf8) } ?? "" - return (process.terminationStatus, stdout, stderr) -} - -private func prepareSandbox() -> (testSwiftURL: URL, seatbeltURL: URL)? { - // Since `/private/tmp` is hard coded in `/usr/local/Homebrew/Library/Homebrew/sandbox.rb`, we use them. - // /private/tmp - // ├── AADA6B05-2E06-4E7F-BA48-8B3AF44415E3 - // │   └── Test.swift - // ├── AADA6B05-2E06-4E7F-BA48-8B3AF44415E3.sb - do { - // `/private/tmp` is standardized to `/tmp` that is symbolic link to `/private/tmp`. - let temporaryDirectoryURL = URL(fileURLWithPath: "/tmp", isDirectory: true) - .appendingPathComponent(UUID().uuidString) - try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true) - - let seatbeltURL = temporaryDirectoryURL.appendingPathExtension("sb") - try sandboxProfile().write(to: seatbeltURL, atomically: true, encoding: .utf8) - - let testSwiftURL = temporaryDirectoryURL.appendingPathComponent("Test.swift") - try "import Foundation".write(to: testSwiftURL, atomically: true, encoding: .utf8) - return (testSwiftURL, seatbeltURL) - } catch { - XCTFail("\(error)") - return nil - } -} - -private func sandboxProfile() -> String { - let homeDirectory = NSHomeDirectory() - return """ - (version 1) - (debug deny) ; log all denied operations to /var/log/system.log - (allow file-write* (subpath "/private/tmp")) - (allow file-write* (subpath "/private/var/tmp")) - (allow file-write* (regex #"^/private/var/folders/[^/]+/[^/]+/[C,T]/")) - (allow file-write* (subpath "/private/tmp")) - (allow file-write* (subpath "\(homeDirectory)/Library/Caches/Homebrew")) - (allow file-write* (subpath "\(homeDirectory)/Library/Logs/Homebrew/swiftlint")) - (allow file-write* (subpath "\(homeDirectory)/Library/Developer")) - (allow file-write* (subpath "/usr/local/var/cache")) - (allow file-write* (subpath "/usr/local/var/homebrew/locks")) - (allow file-write* (subpath "/usr/local/var/log")) - (allow file-write* (subpath "/usr/local/var/run")) - (allow file-write* - (literal "/dev/ptmx") - (literal "/dev/dtracehelper") - (literal "/dev/null") - (literal "/dev/random") - (literal "/dev/zero") - (regex #"^/dev/fd/[0-9]+$") - (regex #"^/dev/ttys?[0-9]*$") - ) - (deny file-write*) ; deny all other file write operations - (allow process-exec - (literal "/bin/ps") - (with no-sandbox) - ) ; allow certain processes running without sandbox - (allow default) ; allow everything else - - """ -} - -private func swiftlintBuiltBySwiftPM() -> URL? { -#if DEBUG - let configuration = "debug" -#else - let configuration = "release" -#endif - let swiftBuildShowBinPathArgs = ["swift", "build", "--show-bin-path", "--configuration", configuration] - let binPathResult = execute(swiftBuildShowBinPathArgs) - guard binPathResult.status == 0 else { - let commandline = swiftBuildShowBinPathArgs.joined(separator: " ") - XCTFail("`\(commandline)` failed with status: \(binPathResult.status), error: \(binPathResult.stderr)") - return nil - } - let binPathString = binPathResult.stdout.components(separatedBy: CharacterSet.newlines).first! - let swiftlint = URL(fileURLWithPath: binPathString).appendingPathComponent("swiftlint") - guard FileManager.default.fileExists(atPath: swiftlint.path) else { - XCTFail("`swiftlint` does not exists.") - return nil - } - return swiftlint -} - -#endif From 1ec3fdc58d7f5a64145f24c317e04dc17b4432e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 20:23:17 +0200 Subject: [PATCH 051/260] Pass options to visitor (#5811) --- .../Configuration+CommandLine.swift | 41 ++++++----- .../LintOrAnalyzeCommand.swift | 9 +-- .../LintableFilesVisitor.swift | 69 +++++-------------- 3 files changed, 44 insertions(+), 75 deletions(-) diff --git a/Source/SwiftLintFramework/Configuration+CommandLine.swift b/Source/SwiftLintFramework/Configuration+CommandLine.swift index 49e62b9a69..6e2d1d526c 100644 --- a/Source/SwiftLintFramework/Configuration+CommandLine.swift +++ b/Source/SwiftLintFramework/Configuration+CommandLine.swift @@ -77,7 +77,7 @@ extension Configuration { -> [Configuration: [SwiftLintFile]] { if files.isEmpty && !visitor.allowZeroLintableFiles { throw SwiftLintError.usageError( - description: "No lintable files found at paths: '\(visitor.paths.joined(separator: ", "))'" + description: "No lintable files found at paths: '\(visitor.options.paths.joined(separator: ", "))'" ) } @@ -141,13 +141,13 @@ extension Configuration { let counter = CounterActor() let total = linters.filter(\.isCollecting).count let progress = ProgressBar(count: total) - if visitor.showProgressBar && total > 0 { + if visitor.options.progress && total > 0 { await progress.initialize() } let collect = { (linter: Linter) -> CollectedLinter? in let skipFile = visitor.shouldSkipFile(atPath: linter.file.path) - if !visitor.quiet && linter.isCollecting { - if visitor.showProgressBar { + if !visitor.options.quiet && linter.isCollecting { + if visitor.options.progress { await progress.printNext() } else if let filePath = linter.file.path { let outputFilename = self.outputFilename(for: filePath, duplicateFileNames: duplicateFileNames) @@ -185,17 +185,19 @@ extension Configuration { duplicateFileNames: Set) async -> [SwiftLintFile] { let counter = CounterActor() let progress = ProgressBar(count: linters.count) - if visitor.showProgressBar { + if visitor.options.progress { await progress.initialize() } let visit = { (linter: CollectedLinter) -> SwiftLintFile in - if !visitor.quiet { - if visitor.showProgressBar { + if !visitor.options.quiet { + if visitor.options.progress { await progress.printNext() } else if let filePath = linter.file.path { let outputFilename = self.outputFilename(for: filePath, duplicateFileNames: duplicateFileNames) let visited = await counter.next() - queuedPrintError("\(visitor.action) '\(outputFilename)' (\(visited)/\(linters.count))") + queuedPrintError( + "\(visitor.options.capitalizedVerb) '\(outputFilename)' (\(visited)/\(linters.count))" + ) } } @@ -210,45 +212,46 @@ extension Configuration { } fileprivate func getFiles(with visitor: LintableFilesVisitor) async throws -> [SwiftLintFile] { - if visitor.useSTDIN { + let options = visitor.options + if options.useSTDIN { let stdinData = FileHandle.standardInput.readDataToEndOfFile() if let stdinString = String(data: stdinData, encoding: .utf8) { return [SwiftLintFile(contents: stdinString)] } throw SwiftLintError.usageError(description: "stdin isn't a UTF8-encoded string") } - if visitor.useScriptInputFiles { + if options.useScriptInputFiles { let files = try scriptInputFiles() - guard visitor.forceExclude else { + guard options.forceExclude else { return files } let scriptInputPaths = files.compactMap(\.path) - if visitor.useExcludingByPrefix { + if options.useExcludingByPrefix { return filterExcludedPathsByPrefix(in: scriptInputPaths) .map(SwiftLintFile.init(pathDeferringReading:)) } return filterExcludedPaths(excludedPaths(), in: scriptInputPaths) .map(SwiftLintFile.init(pathDeferringReading:)) } - if !visitor.quiet { + if !options.quiet { let filesInfo: String - if visitor.paths.isEmpty || visitor.paths == [""] { + if options.paths.isEmpty || options.paths == [""] { filesInfo = "in current working directory" } else { - filesInfo = "at paths \(visitor.paths.joined(separator: ", "))" + filesInfo = "at paths \(options.paths.joined(separator: ", "))" } - queuedPrintError("\(visitor.action) Swift files \(filesInfo)") + queuedPrintError("\(options.capitalizedVerb) Swift files \(filesInfo)") } - let excludeLintableFilesBy = visitor.useExcludingByPrefix + let excludeLintableFilesBy = options.useExcludingByPrefix ? Configuration.ExcludeBy.prefix : .paths(excludedPaths: excludedPaths()) - return visitor.paths.flatMap { + return options.paths.flatMap { self.lintableFiles( inPath: $0, - forceExclude: visitor.forceExclude, + forceExclude: options.forceExclude, excludeBy: excludeLintableFilesBy) } } diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index b2d313233b..26ac2688ce 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -108,10 +108,11 @@ package struct LintOrAnalyzeOptions { } var verb: String { - if autocorrect { - return "correcting" - } - return mode.verb + autocorrect ? "correcting" : mode.verb + } + + var capitalizedVerb: String { + verb.capitalized } } diff --git a/Source/SwiftLintFramework/LintableFilesVisitor.swift b/Source/SwiftLintFramework/LintableFilesVisitor.swift index 97c5a2e535..c6c630e335 100644 --- a/Source/SwiftLintFramework/LintableFilesVisitor.swift +++ b/Source/SwiftLintFramework/LintableFilesVisitor.swift @@ -68,78 +68,43 @@ private func resolveParamsFiles(args: [String]) -> [String] { } struct LintableFilesVisitor { - let paths: [String] - let action: String - let useSTDIN: Bool - let quiet: Bool - let showProgressBar: Bool - let useScriptInputFiles: Bool - let forceExclude: Bool - let useExcludingByPrefix: Bool + let options: LintOrAnalyzeOptions let cache: LinterCache? - let parallel: Bool - let allowZeroLintableFiles: Bool let mode: LintOrAnalyzeModeWithCompilerArguments + let parallel: Bool let block: (CollectedLinter) async -> Void + let allowZeroLintableFiles: Bool - private init(paths: [String], - action: String, - useSTDIN: Bool, - quiet: Bool, - showProgressBar: Bool, - useScriptInputFiles: Bool, - forceExclude: Bool, - useExcludingByPrefix: Bool, + private init(options: LintOrAnalyzeOptions, cache: LinterCache?, - compilerInvocations: CompilerInvocations?, allowZeroLintableFiles: Bool, - block: @escaping (CollectedLinter) async -> Void) { - self.paths = resolveParamsFiles(args: paths) - self.action = action - self.useSTDIN = useSTDIN - self.quiet = quiet - self.showProgressBar = showProgressBar - self.useScriptInputFiles = useScriptInputFiles - self.forceExclude = forceExclude - self.useExcludingByPrefix = useExcludingByPrefix + block: @escaping (CollectedLinter) async -> Void) throws { + self.options = options self.cache = cache - if let compilerInvocations { - self.mode = .analyze(allCompilerInvocations: compilerInvocations) + if options.mode == .lint { + self.mode = .lint + self.parallel = true + } else { + self.mode = .analyze(allCompilerInvocations: try Self.loadCompilerInvocations(options)) // SourceKit had some changes in 5.6 that makes it ~100x more expensive // to process files concurrently. By processing files serially, it's // only 2x slower than before. self.parallel = SwiftVersion.current < .fiveDotSix - } else { - self.mode = .lint - self.parallel = true } - self.block = block self.allowZeroLintableFiles = allowZeroLintableFiles + self.block = block } static func create(_ options: LintOrAnalyzeOptions, cache: LinterCache?, allowZeroLintableFiles: Bool, - block: @escaping (CollectedLinter) async -> Void) - throws -> Self { + block: @escaping (CollectedLinter) async -> Void) throws -> Self { try Signposts.record(name: "LintableFilesVisitor.Create") { - let compilerInvocations: CompilerInvocations? - if options.mode == .lint { - compilerInvocations = nil - } else { - compilerInvocations = try loadCompilerInvocations(options) - } - - return Self( - paths: options.paths, action: options.verb.bridge().capitalized, - useSTDIN: options.useSTDIN, quiet: options.quiet, - showProgressBar: options.progress, - useScriptInputFiles: options.useScriptInputFiles, - forceExclude: options.forceExclude, - useExcludingByPrefix: options.useExcludingByPrefix, + try Self( + options: options, cache: cache, - compilerInvocations: compilerInvocations, - allowZeroLintableFiles: allowZeroLintableFiles, block: block + allowZeroLintableFiles: allowZeroLintableFiles, + block: block ) } } From ab699f98ae8fdb7eb3ae96463ecbf440d482efbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 21:19:23 +0200 Subject: [PATCH 052/260] Use async/await for URL session (#5814) --- .../LintOrAnalyzeCommand.swift | 2 +- Source/SwiftLintFramework/UpdateChecker.swift | 18 ++++-------------- Source/swiftlint/Commands/Version.swift | 2 +- azure-pipelines.yml | 2 -- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 26ac2688ce..eeb59ee19c 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -144,7 +144,7 @@ package struct LintOrAnalyzeCommand { try postProcessViolations(files: files, builder: builder) } if options.checkForUpdates || builder.configuration.checkForUpdates { - UpdateChecker.checkForUpdates() + await UpdateChecker.checkForUpdates() } } diff --git a/Source/SwiftLintFramework/UpdateChecker.swift b/Source/SwiftLintFramework/UpdateChecker.swift index 81043ce36b..ad8db0538c 100644 --- a/Source/SwiftLintFramework/UpdateChecker.swift +++ b/Source/SwiftLintFramework/UpdateChecker.swift @@ -16,9 +16,9 @@ import FoundationNetworking #endif package enum UpdateChecker { - package static func checkForUpdates() { + package static func checkForUpdates() async { guard let url = URL(string: "https://api.github.com/repos/realm/SwiftLint/releases/latest"), - let data = sendRequest(to: url), + let data = try? await sendRequest(to: url), let latestVersionNumber = parseVersionNumber(data) else { print("Could not check latest SwiftLint version") return @@ -39,20 +39,10 @@ package enum UpdateChecker { return jsonObject["tag_name"] as? String } - private static func sendRequest(to url: URL) -> Data? { + private static func sendRequest(to url: URL) async throws -> Data { var request = URLRequest(url: url) request.setValue("SwiftLint", forHTTPHeaderField: "User-Agent") request.setValue("application/vnd.github.v3+json", forHTTPHeaderField: "Accept") - let semaphore = DispatchSemaphore(value: 0) - var result: Data? - - let task = URLSession.shared.dataTask(with: request) { data, _, _ in - result = data - semaphore.signal() - } - task.resume() - - semaphore.wait() - return result + return try await URLSession.shared.data(for: request).0 } } diff --git a/Source/swiftlint/Commands/Version.swift b/Source/swiftlint/Commands/Version.swift index aaa35dd897..0aa32567d1 100644 --- a/Source/swiftlint/Commands/Version.swift +++ b/Source/swiftlint/Commands/Version.swift @@ -20,7 +20,7 @@ extension SwiftLint { print(Self.value) } if checkForUpdates { - UpdateChecker.checkForUpdates() + await UpdateChecker.checkForUpdates() } ExitHelper.successfullyExit() } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f4e8aaa3d9..6fc1eb3f11 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,8 +8,6 @@ jobs: strategy: maxParallel: 10 matrix: - 'Swift 5.10.1': - image: swift:5.10.1-noble 'Swift 6': image: swift:6.0-noble container: $[ variables['image'] ] From b903e0bd8728d489f5cfeea650e272bb4d09bbf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 21:22:07 +0200 Subject: [PATCH 053/260] Sync strict concurrency build settings (#5813) --- BUILD | 2 +- Package.swift | 10 ++++++---- Source/SwiftLintCore/Models/Issue.swift | 2 +- azure-pipelines.yml | 3 --- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/BUILD b/BUILD index 9dd4eacfee..fdcb795575 100644 --- a/BUILD +++ b/BUILD @@ -150,7 +150,7 @@ swift_binary( name = "swiftlint", package_name = "SwiftLint", srcs = glob(["Source/swiftlint/**/*.swift"]), - copts = copts, + copts = copts + strict_concurrency_copts, visibility = ["//visibility:public"], deps = [ ":SwiftLintFramework", diff --git a/Package.swift b/Package.swift index 0dfcc60a62..37a7f3fbe7 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let swiftFeatures: [SwiftSetting] = [ .enableUpcomingFeature("ForwardTrailingClosures"), .enableUpcomingFeature("ImplicitOpenExistentials"), ] +let strictConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency")] let swiftLintPluginDependencies: [Target.Dependency] @@ -55,7 +56,7 @@ let package = Package( "SwiftLintFramework", "SwiftyTextTable", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "CLITests", @@ -88,7 +89,8 @@ let package = Package( ), .target( name: "SwiftLintExtraRules", - dependencies: ["SwiftLintCore"] + dependencies: ["SwiftLintCore"], + swiftSettings: strictConcurrency ), .target( name: "SwiftLintFramework", @@ -100,7 +102,7 @@ let package = Package( .product(name: "ArgumentParser", package: "swift-argument-parser"), "CollectionConcurrencyKit", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .target(name: "DyldWarningWorkaround"), .target( @@ -155,7 +157,7 @@ let package = Package( .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ], path: "Source/SwiftLintCoreMacros", - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "MacroTests", diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index bdb9fbfe94..0954bac837 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -75,7 +75,7 @@ public enum Issue: LocalizedError, Equatable { case baselineNotReadable(path: String) /// Flag to enable warnings for deprecations being printed to the console. Printing is enabled by default. - public static var printDeprecationWarnings = true + package nonisolated(unsafe) static var printDeprecationWarnings = true /// Hook used to capture all messages normally printed to stdout and return them back to the caller. /// diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6fc1eb3f11..3ec9125914 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,9 +19,6 @@ jobs: strategy: maxParallel: 10 matrix: - '13': - image: 'macOS-13' - xcode: '15.2' '14': image: 'macOS-14' xcode: '15.4' From b59e2daad0bc000c14ee38e94fa0a1d9df049729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 21:34:49 +0200 Subject: [PATCH 054/260] Build Docker image with Swift 6 on Ubuntu 24.04 --- .github/workflows/docker.yml | 2 +- Dockerfile | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ed6825e433..47f6f9f583 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Set Docker tag diff --git a/Dockerfile b/Dockerfile index cef955886e..acc6c1e258 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -# Explicitly specify `jammy` to keep the Swift & Ubuntu images in sync. -ARG BUILDER_IMAGE=swift:5.9-jammy -ARG RUNTIME_IMAGE=ubuntu:jammy +# Explicitly specify `noble` to keep the Swift & Ubuntu images in sync. +ARG BUILDER_IMAGE=swift:6.0-noble +ARG RUNTIME_IMAGE=ubuntu:noble # Builder image FROM ${BUILDER_IMAGE} AS builder From 4f2b03b43376098a1038665466e2657a89661166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 29 Sep 2024 23:35:31 +0200 Subject: [PATCH 055/260] Link to internal libraries --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index acc6c1e258..e802cd46a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,13 +15,13 @@ COPY Tests Tests/ COPY Package.* ./ RUN swift package update -ARG SWIFT_FLAGS="-c release -Xswiftc -static-stdlib -Xlinker -lCFURLSessionInterface -Xlinker -lCFXMLInterface -Xlinker -lcurl -Xlinker -lxml2 -Xswiftc -I. -Xlinker -fuse-ld=lld -Xlinker -L/usr/lib/swift/linux" +ARG SWIFT_FLAGS="-c release -Xswiftc -static-stdlib -Xlinker -l_CFURLSessionInterface -Xlinker -l_CFXMLInterface -Xlinker -lcurl -Xlinker -lxml2 -Xswiftc -I. -Xlinker -fuse-ld=lld -Xlinker -L/usr/lib/swift/linux" RUN swift build $SWIFT_FLAGS --product swiftlint RUN mv `swift build $SWIFT_FLAGS --show-bin-path`/swiftlint /usr/bin # Runtime image FROM ${RUNTIME_IMAGE} -LABEL org.opencontainers.image.source https://github.com/realm/SwiftLint +LABEL org.opencontainers.image.source=https://github.com/realm/SwiftLint RUN apt-get update && apt-get install -y \ libcurl4 \ libxml2 \ From 6d1cb6062143335280fd85ec80eb37168d1c4a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Sep 2024 13:32:42 +0200 Subject: [PATCH 056/260] Copy more required dependencies --- Dockerfile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Dockerfile b/Dockerfile index e802cd46a0..146675d8e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,20 +30,31 @@ COPY --from=builder /usr/lib/libsourcekitdInProc.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftBasicFormat.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftCompilerPluginMessageHandling.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftDiagnostics.so /usr/lib +COPY --from=builder /usr/lib/swift/host/libSwiftIDEUtils.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftOperators.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftParser.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftParserDiagnostics.so /usr/lib +COPY --from=builder /usr/lib/swift/host/libSwiftRefactor.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftSyntax.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftSyntaxBuilder.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftSyntaxMacroExpansion.so /usr/lib COPY --from=builder /usr/lib/swift/host/libSwiftSyntaxMacros.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/lib_FoundationICU.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libBlocksRuntime.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libdispatch.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libFoundation.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libFoundationInternationalization.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libFoundationEssentials.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libFoundationNetworking.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libFoundationXML.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libswift_Concurrency.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libswift_RegexParser.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libswift_StringProcessing.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libswiftCore.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libswiftDispatch.so /usr/lib COPY --from=builder /usr/lib/swift/linux/libswiftGlibc.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libswiftSynchronization.so /usr/lib +COPY --from=builder /usr/lib/swift/linux/libswiftSwiftOnoneSupport.so /usr/lib COPY --from=builder /usr/bin/swiftlint /usr/bin RUN swiftlint version From c4a732583a01e4f389a1e6b8402dff4bc471431f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Sep 2024 14:35:34 +0200 Subject: [PATCH 057/260] Wrap buffer to make it @Sendable (#5815) --- .../Extensions/Array+SwiftLint.swift | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift index e06fb18ea2..9981e91bdd 100644 --- a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift @@ -103,10 +103,36 @@ public extension Array { func parallelMap(transform: (Element) -> T) -> [T] { var result = ContiguousArray(repeating: nil, count: count) return result.withUnsafeMutableBufferPointer { buffer in + let buffer = Wrapper(buffer: buffer) DispatchQueue.concurrentPerform(iterations: buffer.count) { idx in buffer[idx] = transform(self[idx]) } - return buffer.map { $0! } + return buffer.data + } + } + + private class Wrapper: @unchecked Sendable { + let buffer: UnsafeMutableBufferPointer + + init(buffer: UnsafeMutableBufferPointer) { + self.buffer = buffer + } + + var data: [T] { + buffer.map { $0! } + } + + var count: Int { + buffer.count + } + + subscript(index: Int) -> T { + get { + queuedFatalError("Do not call this getter.") + } + set(newValue) { + buffer[index] = newValue + } } } } From 63ee1944bd40ed50aaae3d8b1a8e2c60090f119f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 1 Oct 2024 17:25:03 +0200 Subject: [PATCH 058/260] Keep Swift 5 macOS build for the time being (#5816) --- Source/SwiftLintCore/Models/Issue.swift | 4 ++++ azure-pipelines.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index 0954bac837..cb5e8615c4 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -75,7 +75,11 @@ public enum Issue: LocalizedError, Equatable { case baselineNotReadable(path: String) /// Flag to enable warnings for deprecations being printed to the console. Printing is enabled by default. + #if compiler(>=6.0) package nonisolated(unsafe) static var printDeprecationWarnings = true + #else + package static var printDeprecationWarnings = true + #endif /// Hook used to capture all messages normally printed to stdout and return them back to the caller. /// diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3ec9125914..6fc1eb3f11 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,6 +19,9 @@ jobs: strategy: maxParallel: 10 matrix: + '13': + image: 'macOS-13' + xcode: '15.2' '14': image: 'macOS-14' xcode: '15.4' From 88c2e4c1ce6033f6c242098ccf1a05ee219283f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 4 Oct 2024 15:29:29 +0200 Subject: [PATCH 059/260] Enable strict concurrency in built-in rules module (#5817) --- BUILD | 5 +---- Package.swift | 2 +- Source/SwiftLintFramework/LintOrAnalyzeCommand.swift | 3 +++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BUILD b/BUILD index fdcb795575..02e28f9cb1 100644 --- a/BUILD +++ b/BUILD @@ -103,10 +103,7 @@ swift_library( name = "SwiftLintBuiltInRules", package_name = "SwiftLint", srcs = glob(["Source/SwiftLintBuiltInRules/**/*.swift"]), - copts = copts + select({ - ":strict_concurrency_builtin_rules": strict_concurrency_copts, - "//conditions:default": [], - }), + copts = copts + strict_concurrency_copts, module_name = "SwiftLintBuiltInRules", visibility = ["//visibility:public"], deps = [ diff --git a/Package.swift b/Package.swift index 37a7f3fbe7..2bad9f4e92 100644 --- a/Package.swift +++ b/Package.swift @@ -85,7 +85,7 @@ let package = Package( .target( name: "SwiftLintBuiltInRules", dependencies: ["SwiftLintCore"], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .target( name: "SwiftLintExtraRules", diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index eeb59ee19c..8889fa3251 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -1,3 +1,6 @@ +#if os(macOS) +@preconcurrency import Darwin +#endif import Dispatch import Foundation From d22a719d76f2e13288d48d679673cf526b8d5198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 6 Oct 2024 22:51:40 +0200 Subject: [PATCH 060/260] Build with warnings as errors excluding SourceKitten (#5818) --- BUILD | 13 ++++++++++++- Source/SourceKittenFrameworkWrapper/Empty.swift | 0 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Source/SourceKittenFrameworkWrapper/Empty.swift diff --git a/BUILD b/BUILD index 02e28f9cb1..f6e1b93ccf 100644 --- a/BUILD +++ b/BUILD @@ -26,6 +26,7 @@ config_setting( ) copts = [ + "-warnings-as-errors", "-enable-upcoming-feature", "ExistentialAny", "-enable-upcoming-feature", @@ -90,7 +91,7 @@ swift_library( "@SwiftSyntax//:SwiftParserDiagnostics_opt", "@SwiftSyntax//:SwiftSyntaxBuilder_opt", "@SwiftSyntax//:SwiftSyntax_opt", - "@com_github_jpsim_sourcekitten//:SourceKittenFramework", + ":SourceKittenFramework.wrapper", "@sourcekitten_com_github_jpsim_yams//:Yams", "@swiftlint_com_github_scottrhoyt_swifty_text_table//:SwiftyTextTable", ] + select({ @@ -99,6 +100,16 @@ swift_library( }), ) +swift_library( + name = "SourceKittenFramework.wrapper", + srcs = ["Source/SourceKittenFrameworkWrapper/Empty.swift"], + module_name = "SourceKittenFrameworkWrapper", + visibility = ["//visibility:public"], + deps = [ + "@com_github_jpsim_sourcekitten//:SourceKittenFramework", + ], +) + swift_library( name = "SwiftLintBuiltInRules", package_name = "SwiftLint", diff --git a/Source/SourceKittenFrameworkWrapper/Empty.swift b/Source/SourceKittenFrameworkWrapper/Empty.swift new file mode 100644 index 0000000000..e69de29bb2 From 3b6ba6d1f042bc7baeb470da7417c7204aa9c9b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 7 Oct 2024 21:55:57 +0200 Subject: [PATCH 061/260] Fix warning for unused return value in Linux build (#5819) --- Source/swiftlint/Commands/SwiftLint.swift | 6 ++++- .../ConfigurationTests+MultipleConfigs.swift | 18 +++++++------- .../ConfigurationTests.swift | 24 +++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Source/swiftlint/Commands/SwiftLint.swift b/Source/swiftlint/Commands/SwiftLint.swift index 7368b9a9d8..c0b0772533 100644 --- a/Source/swiftlint/Commands/SwiftLint.swift +++ b/Source/swiftlint/Commands/SwiftLint.swift @@ -10,7 +10,11 @@ import SwiftLintFramework struct SwiftLint: AsyncParsableCommand { static let configuration: CommandConfiguration = { if let directory = ProcessInfo.processInfo.environment["BUILD_WORKSPACE_DIRECTORY"] { - FileManager.default.changeCurrentDirectoryPath(directory) + if !FileManager.default.changeCurrentDirectoryPath(directory) { + queuedFatalError(""" + Could not change current directory to \(directory) specified by BUILD_WORKSPACE_DIRECTORY. + """) + } } RuleRegistry.registerAllRulesOnce() diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift index 4351b19786..a5c1328741 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift @@ -237,7 +237,7 @@ extension ConfigurationTests { } for path in [Mock.Dir.childConfigTest1, Mock.Dir.childConfigTest2] { - FileManager.default.changeCurrentDirectoryPath(path) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(path)) assertEqualExceptForFileGraph( Configuration(configurationFiles: ["main.yml"]), @@ -248,7 +248,7 @@ extension ConfigurationTests { func testValidParentConfig() { for path in [Mock.Dir.parentConfigTest1, Mock.Dir.parentConfigTest2] { - FileManager.default.changeCurrentDirectoryPath(path) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(path)) assertEqualExceptForFileGraph( Configuration(configurationFiles: ["main.yml"]), @@ -263,7 +263,7 @@ extension ConfigurationTests { } for path in [Mock.Dir.childConfigTest1, Mock.Dir.childConfigTest2] { - FileManager.default.changeCurrentDirectoryPath(path) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(path)) assertEqualExceptForFileGraph( Configuration( @@ -283,7 +283,7 @@ extension ConfigurationTests { Mock.Dir.parentConfigCycle2, Mock.Dir.parentConfigCycle3, ] { - FileManager.default.changeCurrentDirectoryPath(path) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(path)) // If the cycle is properly detected, the config should equal the default config. XCTAssertEqual( @@ -294,7 +294,7 @@ extension ConfigurationTests { } func testCommandLineConfigsCycleDetection() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.childConfigCycle4) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.childConfigCycle4)) // If the cycle is properly detected, the config should equal the default config. assertEqualExceptForFileGraph( @@ -525,7 +525,7 @@ extension ConfigurationTests { // MARK: - Remote Configs func testValidRemoteChildConfig() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigChild) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigChild)) assertEqualExceptForFileGraph( Configuration( @@ -544,7 +544,7 @@ extension ConfigurationTests { } func testValidRemoteParentConfig() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigParent) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigParent)) assertEqualExceptForFileGraph( Configuration( @@ -569,7 +569,7 @@ extension ConfigurationTests { } func testsRemoteConfigNotAllowedToReferenceLocalConfig() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigLocalRef) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigLocalRef)) // If the remote file is not allowed to reference a local file, the config should equal the default config. XCTAssertEqual( @@ -589,7 +589,7 @@ extension ConfigurationTests { } func testRemoteConfigCycleDetection() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigCycle) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.remoteConfigCycle)) // If the cycle is properly detected, the config should equal the default config. XCTAssertEqual( diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index 16fe56e7cf..f0ea288ba0 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -15,12 +15,12 @@ final class ConfigurationTests: SwiftLintTestCase { super.setUp() Configuration.resetCache() previousWorkingDir = FileManager.default.currentDirectoryPath - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) } override func tearDown() { super.tearDown() - FileManager.default.changeCurrentDirectoryPath(previousWorkingDir) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(previousWorkingDir)) } // MARK: Tests @@ -37,7 +37,7 @@ final class ConfigurationTests: SwiftLintTestCase { func testNoConfiguration() { // Change to a folder where there is no `.swiftlint.yml` - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.emptyFolder) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.emptyFolder)) // Test whether the default configuration is used if there is no `.swiftlint.yml` or other config file XCTAssertEqual(Configuration(configurationFiles: []), Configuration.default) @@ -210,7 +210,7 @@ final class ConfigurationTests: SwiftLintTestCase { return } - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level1) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level1)) // The included path "File.swift" should be put relative to the configuration file // (~> Resources/ProjectMock/File.swift) and not relative to the path where @@ -230,7 +230,7 @@ final class ConfigurationTests: SwiftLintTestCase { func testIncludedExcludedRelativeLocationLevel0() { // Same as testIncludedPathRelatedToConfigurationFileLocationLevel1(), // but run from the directory the config file resides in - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration(configurationFiles: ["custom_included_excluded.yml"]) let actualIncludedPath = configuration.includedPaths.first!.bridge() .absolutePathRepresentation(rootDirectory: configuration.rootDirectory) @@ -345,7 +345,7 @@ final class ConfigurationTests: SwiftLintTestCase { } func testGlobIncludePaths() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration(includedPaths: ["**/Level2"]) let paths = configuration.lintablePaths(inPath: Mock.Dir.level0, forceExclude: true, @@ -468,7 +468,7 @@ final class ConfigurationTests: SwiftLintTestCase { // MARK: - ExcludeByPrefix option tests extension ConfigurationTests { func testExcludeByPrefixExcludedPaths() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration( includedPaths: ["Level1"], excludedPaths: ["Level1/Level1.swift", "Level1/Level2/Level3"] @@ -481,7 +481,7 @@ extension ConfigurationTests { } func testExcludeByPrefixForceExcludesFile() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration(excludedPaths: ["Level1/Level2/Level3/Level3.swift"]) let paths = configuration.lintablePaths(inPath: "Level1/Level2/Level3/Level3.swift", forceExclude: true, @@ -490,7 +490,7 @@ extension ConfigurationTests { } func testExcludeByPrefixForceExcludesFileNotPresentInExcluded() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration(includedPaths: ["Level1"], excludedPaths: ["Level1/Level1.swift"]) let paths = configuration.lintablePaths(inPath: "Level1", @@ -501,7 +501,7 @@ extension ConfigurationTests { } func testExcludeByPrefixForceExcludesDirectory() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration( excludedPaths: [ "Level1/Level2", "Directory.swift", "ChildConfig", "ParentConfig", "NestedConfig" @@ -515,7 +515,7 @@ extension ConfigurationTests { } func testExcludeByPrefixForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration( excludedPaths: [ "Level1", "Directory.swift/DirectoryLevel1.swift", "ChildConfig", "ParentConfig", "NestedConfig" @@ -529,7 +529,7 @@ extension ConfigurationTests { } func testExcludeByPrefixGlobExcludePaths() { - FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0) + XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0)) let configuration = Configuration( includedPaths: ["Level1"], excludedPaths: ["Level1/*/*.swift", "Level1/*/*/*.swift"]) From 0a1ee180da3787418fbcd3e85013b4ac04e48c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 7 Oct 2024 22:14:46 +0200 Subject: [PATCH 062/260] Separate build and copy steps from each other (#5820) --- tools/oss-check | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tools/oss-check b/tools/oss-check index 1bf0c65e7c..77a70c8365 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -97,15 +97,12 @@ def fail(str) exit end -def perform(*args) - commands = args - if @options[:verbose] - commands.each do |x| - puts(x) - system(x) - end +def perform(command, dir: nil) + puts command if @options[:verbose] + if dir + Dir.chdir(dir) { perform(command) } else - commands.each { |x| `#{x}` } + system(command) end end @@ -208,24 +205,24 @@ def build(branch) dir = "#{@working_dir}/builds" target = branch == 'main' ? @effective_main_commitish : @options[:branch] if File.directory?(dir) - perform("cd #{dir}; git checkout #{target}") + perform("git checkout #{target}", dir: dir) else perform("git worktree add --detach #{dir} #{target}") end - build_command = "cd #{dir}; bazel build --config=release @SwiftLint//:swiftlint && mv bazel-bin/swiftlint swiftlint-#{branch}" - - perform(build_command) - return if $?.success? + build_command = "bazel build --config=release @SwiftLint//:swiftlint" return_value = nil puts build_command if @options[:verbose] - Open3.popen3(build_command) do |_, stdout, _, wait_thr| + Open3.popen3(build_command, :chdir=>"#{dir}") do |_, stdout, stderr, wait_thr| puts stdout.read.chomp + puts stderr.read.chomp return_value = wait_thr.value end fail "Could not build #{branch}" unless return_value.success? + + perform("mv bazel-bin/swiftlint swiftlint-#{branch}", dir: dir) end def diff_and_report_changes_to_danger From 9ebd6ae9d20798b111bd7088e3c247be4473b006 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Fri, 11 Oct 2024 19:15:48 +0100 Subject: [PATCH 063/260] Dont trigger for non-optional type casting, or for optional types (#5805) --- CHANGELOG.md | 5 ++++ .../Idiomatic/PreferTypeCheckingRule.swift | 26 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7257d37b63..3596e31d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,11 @@ [chipp](https://github.com/chipp) [#5791](https://github.com/realm/SwiftLint/issues/5791) +* The `prefer_type_checking` rule will no longer trigger for non-optional + type casting (`as`), or for comparisons to optional types. + [Martin Redington](https://github.com/mildm8nnered) + [#5802](https://github.com/realm/SwiftLint/issues/5802) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift index c0ae6731d7..c595dd0db8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift @@ -24,6 +24,10 @@ struct PreferTypeCheckingRule: Rule { foo.run() } """), + Example("bar as Foo != nil"), + Example("nil != bar as Foo"), + Example("bar as Foo? != nil"), + Example("bar as? Foo? != nil"), ], triggeringExamples: [ Example("bar ↓as? Foo != nil"), @@ -33,9 +37,12 @@ struct PreferTypeCheckingRule: Rule { doSomeThing() } """), + Example("nil != bar ↓as? Foo"), + Example("nil != 2*x ↓as? X"), ], corrections: [ Example("bar ↓as? Foo != nil"): Example("bar is Foo"), + Example("nil != bar ↓as? Foo"): Example("bar is Foo"), Example("2*x ↓as? X != nil"): Example("2*x is X"), Example(""" if foo ↓as? Bar != nil { @@ -53,7 +60,7 @@ struct PreferTypeCheckingRule: Rule { private extension PreferTypeCheckingRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: InfixOperatorExprSyntax) { - if node.typeChecksWithAsCasting, let asExpr = node.leftOperand.as(AsExprSyntax.self) { + if let asExpr = node.asExprWithOptionalTypeChecking { violations.append(asExpr.asKeyword.positionAfterSkippingLeadingTrivia) } } @@ -61,8 +68,7 @@ private extension PreferTypeCheckingRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: InfixOperatorExprSyntax) -> ExprSyntax { - guard node.typeChecksWithAsCasting, - let asExpr = node.leftOperand.as(AsExprSyntax.self) else { + guard let asExpr = node.asExprWithOptionalTypeChecking else { return super.visit(node) } @@ -79,9 +85,15 @@ private extension PreferTypeCheckingRule { } private extension InfixOperatorExprSyntax { - var typeChecksWithAsCasting: Bool { - self.leftOperand.is(AsExprSyntax.self) - && self.operator.as(BinaryOperatorExprSyntax.self)?.operator.tokenKind == .binaryOperator("!=") - && self.rightOperand.is(NilLiteralExprSyntax.self) + var asExprWithOptionalTypeChecking: AsExprSyntax? { + if let asExpr = leftOperand.as(AsExprSyntax.self) ?? rightOperand.as(AsExprSyntax.self), + asExpr.questionOrExclamationMark?.tokenKind == .postfixQuestionMark, + !asExpr.type.is(OptionalTypeSyntax.self), + self.operator.as(BinaryOperatorExprSyntax.self)?.operator.tokenKind == .binaryOperator("!="), + rightOperand.is(NilLiteralExprSyntax.self) || leftOperand.is(NilLiteralExprSyntax.self) { + asExpr + } else { + nil + } } } From 9c2d0d5f86f29170ec5cc69235e3f1b5da6dac85 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Tue, 15 Oct 2024 18:56:39 +0100 Subject: [PATCH 064/260] Fix nested disable commands improved (#5797) --- CHANGELOG.md | 5 + Source/SwiftLintCore/Models/Linter.swift | 101 +++++++++++++----- .../SwiftLintCore/Models/RuleIdentifier.swift | 8 +- .../CustomRulesTests.swift | 77 +++++++++++++ 4 files changed, 166 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3596e31d18..e32680d436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,11 @@ [Martin Redington](https://github.com/mildm8nnered) [#5802](https://github.com/realm/SwiftLint/issues/5802) +* Fixes an issue where the `superfluous_disable_command` rule could generate + false positives for nested disable commands for custom rules. + [Martin Redington](https://github.com/mildm8nnered) + [#5788](https://github.com/realm/SwiftLint/issues/5788) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintCore/Models/Linter.swift b/Source/SwiftLintCore/Models/Linter.swift index 23bfad7d54..c87065a557 100644 --- a/Source/SwiftLintCore/Models/Linter.swift +++ b/Source/SwiftLintCore/Models/Linter.swift @@ -1,6 +1,8 @@ import Foundation import SourceKittenFramework +// swiftlint:disable file_length + private let warnSourceKitFailedOnceImpl: Void = { Issue.genericWarning("SourceKit-based rules will be skipped because sourcekitd has failed.").print() }() @@ -23,6 +25,8 @@ private extension Rule { return [] } + let regions = regions.perIdentifierRegions + let regionsDisablingSuperfluousDisableRule = regions.filter { region in region.isRuleDisabled(superfluousDisableCommandRule) } @@ -32,33 +36,31 @@ private extension Rule { if regionsDisablingSuperfluousDisableRule.contains(where: { $0.contains(region.start) }) { continue } - let sortedDisabledIdentifiers = region.disabledRuleIdentifiers.sorted { - $0.stringRepresentation < $1.stringRepresentation + guard let disabledRuleIdentifier = region.disabledRuleIdentifiers.first else { + continue } - commandIDsLoop: for disabledIdentifier in sortedDisabledIdentifiers { - guard !isEnabled(in: region, for: disabledIdentifier.stringRepresentation) else { - continue - } - var disableCommandValid = false - for violation in allViolations where region.contains(violation.location) { - if canBeDisabled(violation: violation, by: disabledIdentifier) { - disableCommandValid = true - continue commandIDsLoop - } + guard !isEnabled(in: region, for: disabledRuleIdentifier.stringRepresentation) else { + continue + } + var disableCommandValid = false + for violation in allViolations where region.contains(violation.location) { + if canBeDisabled(violation: violation, by: disabledRuleIdentifier) { + disableCommandValid = true + break } - if !disableCommandValid { - let reason = superfluousDisableCommandRule.reason( - forRuleIdentifier: disabledIdentifier.stringRepresentation - ) - superfluousDisableCommandViolations.append( - StyleViolation( - ruleDescription: type(of: superfluousDisableCommandRule).description, - severity: superfluousDisableCommandRule.configuration.severity, - location: region.start, - reason: reason - ) + } + if !disableCommandValid { + let reason = superfluousDisableCommandRule.reason( + forRuleIdentifier: disabledRuleIdentifier.stringRepresentation + ) + superfluousDisableCommandViolations.append( + StyleViolation( + ruleDescription: type(of: superfluousDisableCommandRule).description, + severity: superfluousDisableCommandRule.configuration.severity, + location: region.start, + reason: reason ) - } + ) } } return superfluousDisableCommandViolations @@ -147,6 +149,57 @@ private extension Rule { } } +private extension [Region] { + // Normally regions correspond to changes in the set of enabled rules. To detect superfluous disable command + // rule violations effectively, we need individual regions for each disabled rule identifier. + var perIdentifierRegions: [Region] { + guard isNotEmpty else { + return [] + } + + var convertedRegions = [Region]() + var startMap: [RuleIdentifier: Location] = [:] + var lastRegionEnd: Location? + + for region in self { + let ruleIdentifiers = startMap.keys.sorted() + for ruleIdentifier in ruleIdentifiers where !region.disabledRuleIdentifiers.contains(ruleIdentifier) { + if let lastRegionEnd, let start = startMap[ruleIdentifier] { + let newRegion = Region(start: start, end: lastRegionEnd, disabledRuleIdentifiers: [ruleIdentifier]) + convertedRegions.append(newRegion) + startMap[ruleIdentifier] = nil + } + } + for ruleIdentifier in region.disabledRuleIdentifiers where startMap[ruleIdentifier] == nil { + startMap[ruleIdentifier] = region.start + } + if region.disabledRuleIdentifiers.isEmpty { + convertedRegions.append(region) + } + lastRegionEnd = region.end + } + + let end = Location(file: first?.start.file, line: .max, character: .max) + for ruleIdentifier in startMap.keys.sorted() { + if let start = startMap[ruleIdentifier] { + let newRegion = Region(start: start, end: end, disabledRuleIdentifiers: [ruleIdentifier]) + convertedRegions.append(newRegion) + startMap[ruleIdentifier] = nil + } + } + + return convertedRegions.sorted { + if $0.start == $1.start { + if let lhsDisabledRuleIdentifier = $0.disabledRuleIdentifiers.first, + let rhsDisabledRuleIdentifier = $1.disabledRuleIdentifiers.first { + return lhsDisabledRuleIdentifier < rhsDisabledRuleIdentifier + } + } + return $0.start < $1.start + } + } +} + /// Represents a file that can be linted for style violations and corrections after being collected. public struct Linter { /// The file to lint with this linter. diff --git a/Source/SwiftLintCore/Models/RuleIdentifier.swift b/Source/SwiftLintCore/Models/RuleIdentifier.swift index c4ee97f5ba..8955c8badf 100644 --- a/Source/SwiftLintCore/Models/RuleIdentifier.swift +++ b/Source/SwiftLintCore/Models/RuleIdentifier.swift @@ -1,5 +1,5 @@ /// An identifier representing a SwiftLint rule, or all rules. -public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral { +public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral, Comparable { // MARK: - Values /// Special identifier that should be treated as referring to 'all' SwiftLint rules. One helpful usecase is in @@ -39,4 +39,10 @@ public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral { public init(stringLiteral value: String) { self = Self(value) } + + // MARK: - Comparable Conformance + + public static func < (lhs: Self, rhs: Self) -> Bool { + lhs.stringRepresentation < rhs.stringRepresentation + } } diff --git a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift index 3337822047..548fb98db2 100644 --- a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift @@ -427,6 +427,83 @@ final class CustomRulesTests: SwiftLintTestCase { XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) } + func testNestedCustomRuleDisablesDoNotTriggerSuperfluousDisableCommand() throws { + let customRules: [String: Any] = [ + "rule1": [ + "regex": "pattern1" + ], + "rule2": [ + "regex": "pattern2" + ], + ] + let example = Example(""" + // swiftlint:disable rule1 + // swiftlint:disable rule2 + let pattern2 = "" + // swiftlint:enable rule2 + let pattern1 = "" + // swiftlint:enable rule1 + """) + XCTAssertTrue(try violations(forExample: example, customRules: customRules).isEmpty) + } + + func testNestedAndOverlappingCustomRuleDisables() throws { + let customRules: [String: Any] = [ + "rule1": [ + "regex": "pattern1" + ], + "rule2": [ + "regex": "pattern2" + ], + "rule3": [ + "regex": "pattern3" + ], + ] + let example = Example(""" + // swiftlint:disable rule1 + // swiftlint:disable rule2 + // swiftlint:disable rule3 + let pattern2 = "" + // swiftlint:enable rule2 + // swiftlint:enable rule3 + let pattern1 = "" + // swiftlint:enable rule1 + """) + let violations = try violations(forExample: example, customRules: customRules) + + XCTAssertEqual(violations.count, 1) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "rule3")) + } + + func testSuperfluousDisableRuleOrder() throws { + let customRules: [String: Any] = [ + "rule1": [ + "regex": "pattern1" + ], + "rule2": [ + "regex": "pattern2" + ], + "rule3": [ + "regex": "pattern3" + ], + ] + let example = Example(""" + // swiftlint:disable rule1 + // swiftlint:disable rule2 rule3 + // swiftlint:enable rule3 rule2 + // swiftlint:disable rule2 + // swiftlint:enable rule1 + // swiftlint:enable rule2 + """) + let violations = try violations(forExample: example, customRules: customRules) + + XCTAssertEqual(violations.count, 4) + XCTAssertTrue(violations[0].isSuperfluousDisableCommandViolation(for: "rule1")) + XCTAssertTrue(violations[1].isSuperfluousDisableCommandViolation(for: "rule2")) + XCTAssertTrue(violations[2].isSuperfluousDisableCommandViolation(for: "rule3")) + XCTAssertTrue(violations[3].isSuperfluousDisableCommandViolation(for: "rule2")) + } + // MARK: - Private private func getCustomRules(_ extraConfig: [String: Any] = [:]) -> (Configuration, CustomRules) { From 5070b8257c0adb9e06ee43122aa9f5b91ecb2623 Mon Sep 17 00:00:00 2001 From: timesince Date: Fri, 18 Oct 2024 04:34:19 +0800 Subject: [PATCH 065/260] Remove unnecessary symbol (#5827) --- .../Extensions/RandomAccessCollection+Swiftlint.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SwiftLintCore/Extensions/RandomAccessCollection+Swiftlint.swift b/Source/SwiftLintCore/Extensions/RandomAccessCollection+Swiftlint.swift index 1678cb8f3f..e347d3f38b 100644 --- a/Source/SwiftLintCore/Extensions/RandomAccessCollection+Swiftlint.swift +++ b/Source/SwiftLintCore/Extensions/RandomAccessCollection+Swiftlint.swift @@ -24,7 +24,7 @@ public extension RandomAccessCollection where Index == Int { @inlinable func firstIndexAssumingSorted(where predicate: (Self.Element) throws -> Bool) rethrows -> Int? { // Predicate should divide a collection to two pairs of values - // "bad" values for which predicate returns `false`` + // "bad" values for which predicate returns `false` // "good" values for which predicate return `true` // false false false false false true true true From 7385beaaf6579c67f74d4437cece2f9214de2c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 17 Oct 2024 23:13:17 +0200 Subject: [PATCH 066/260] Add validation hook to configuration parsing (#5824) --- Source/SwiftLintCore/Models/Issue.swift | 13 ++++++++++--- .../SwiftLintCore/Protocols/RuleConfiguration.swift | 8 ++++++++ .../RuleConfigurationMacros.swift | 3 +++ Tests/MacroTests/AutoConfigParserTests.swift | 5 +++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index cb5e8615c4..2a35b36863 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -3,7 +3,7 @@ import Foundation /// All possible SwiftLint issues which are printed as warnings by default. public enum Issue: LocalizedError, Equatable { /// The configuration didn't match internal expectations. - case invalidConfiguration(ruleID: String) + case invalidConfiguration(ruleID: String, message: String? = nil) /// Issued when an option is deprecated. Suggests an alternative optionally. case deprecatedConfigurationOption(ruleID: String, key: String, alternative: String? = nil) @@ -20,6 +20,10 @@ public enum Issue: LocalizedError, Equatable { /// Some configuration keys are invalid. case invalidConfigurationKeys(ruleID: String, keys: Set) + /// The configuration is inconsistent, that is options are mutually exclusive or one drives other values + /// irrelevant. + case inconsistentConfiguration(ruleID: String, message: String) + /// Used rule IDs are invalid. case invalidRuleIDs(Set) @@ -138,8 +142,9 @@ public enum Issue: LocalizedError, Equatable { private var message: String { switch self { - case let .invalidConfiguration(id): - return "Invalid configuration for '\(id)' rule. Falling back to default." + case let .invalidConfiguration(id, message): + let message = if let message { ": \(message)" } else { "." } + return "Invalid configuration for '\(id)' rule\(message) Falling back to default." case let .deprecatedConfigurationOption(id, key, alternative): let baseMessage = "Configuration option '\(key)' in '\(id)' rule is deprecated." if let alternative { @@ -154,6 +159,8 @@ public enum Issue: LocalizedError, Equatable { return "'\(old)' has been renamed to '\(new)' and will be completely removed in a future release." case let .invalidConfigurationKeys(id, keys): return "Configuration for '\(id)' rule contains the invalid key(s) \(keys.formatted)." + case let .inconsistentConfiguration(id, message): + return "Inconsistent configuration for '\(id)' rule: \(message)" case let .invalidRuleIDs(ruleIDs): return "The key(s) \(ruleIDs.formatted) used as rule identifier(s) is/are invalid." case let .ruleNotPresentInOnlyRules(id): diff --git a/Source/SwiftLintCore/Protocols/RuleConfiguration.swift b/Source/SwiftLintCore/Protocols/RuleConfiguration.swift index acdd2b6359..c750417df4 100644 --- a/Source/SwiftLintCore/Protocols/RuleConfiguration.swift +++ b/Source/SwiftLintCore/Protocols/RuleConfiguration.swift @@ -13,6 +13,10 @@ public protocol RuleConfiguration: Equatable { /// /// - throws: Throws if the configuration is not in the expected format. mutating func apply(configuration: Any) throws + + /// Run a sanity check on the configuration, perform optional postprocessing steps and/or warn about potential + /// issues. + mutating func validate() throws } /// A configuration for a rule that allows to configure at least the severity. @@ -30,6 +34,10 @@ public extension SeverityBasedRuleConfiguration { public extension RuleConfiguration { var parameterDescription: RuleConfigurationDescription? { nil } + + func validate() throws { + // Do nothing by default. + } } public extension RuleConfiguration { diff --git a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift index e1a44256e8..51ba4427d7 100644 --- a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift +++ b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift @@ -85,6 +85,9 @@ enum AutoConfigParser: MemberMacro { Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } """ + """ + try validate() + """ }), ] } diff --git a/Tests/MacroTests/AutoConfigParserTests.swift b/Tests/MacroTests/AutoConfigParserTests.swift index 4164eb411f..86ce276120 100644 --- a/Tests/MacroTests/AutoConfigParserTests.swift +++ b/Tests/MacroTests/AutoConfigParserTests.swift @@ -44,6 +44,7 @@ final class AutoConfigParserTests: XCTestCase { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } + try validate() } } """, @@ -90,6 +91,7 @@ final class AutoConfigParserTests: XCTestCase { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } + try validate() } } """, @@ -145,6 +147,7 @@ final class AutoConfigParserTests: XCTestCase { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } + try validate() } } """, @@ -171,6 +174,7 @@ final class AutoConfigParserTests: XCTestCase { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } + try validate() } } """, @@ -228,6 +232,7 @@ final class AutoConfigParserTests: XCTestCase { let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys).print() } + try validate() } } """, From b4866488faa79d8b1369cae925f7daa133705809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 19 Oct 2024 19:56:59 +0200 Subject: [PATCH 067/260] Remove workaround (#5830) --- Package.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Package.swift b/Package.swift index 2bad9f4e92..2789c2fef6 100644 --- a/Package.swift +++ b/Package.swift @@ -98,8 +98,6 @@ let package = Package( "SwiftLintBuiltInRules", "SwiftLintCore", "SwiftLintExtraRules", - // Workaround for https://github.com/apple/swift-package-manager/issues/6940: - .product(name: "ArgumentParser", package: "swift-argument-parser"), "CollectionConcurrencyKit", ], swiftSettings: swiftFeatures + strictConcurrency From d4b41bc53b5ff2d2490f51196f5658eb4c0d9473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 24 Oct 2024 18:50:18 +0200 Subject: [PATCH 068/260] Stop triggering on `self` in key path expressions (#5836) --- CHANGELOG.md | 5 +++++ .../SelfInPropertyInitializationRule.swift | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e32680d436..3ca68bc5cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5787](https://github.com/realm/SwiftLint/issues/5787) +* Do not trigger `self_in_property_initialization` rule on `self` in + key paths expressions. + [SimplyDanny](https://github.com/SimplyDanny) + [#5835](https://github.com/realm/SwiftLint/issues/5835) + * Do not throw deprecation warning if deprecated property is not presented in configuration. [chipp](https://github.com/chipp) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/SelfInPropertyInitializationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/SelfInPropertyInitializationRule.swift index cc39c1962b..74082ee95c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/SelfInPropertyInitializationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/SelfInPropertyInitializationRule.swift @@ -68,6 +68,12 @@ struct SelfInPropertyInitializationRule: Rule { func calculateB() -> String { "B" } } """, excludeFromDocumentation: true), + Example(""" + final class NotActuallyReferencingSelf { + let keyPath: Any = \\String.self + let someType: Any = String.self + } + """, excludeFromDocumentation: true), ], triggeringExamples: [ Example(""" @@ -102,7 +108,7 @@ private extension SelfInPropertyInitializationRule { return } - let visitor = IdentifierUsageVisitor(identifier: .keyword(.self)) + let visitor = IdentifierUsageVisitor(viewMode: .sourceAccurate) for binding in node.bindings { guard let initializer = binding.initializer, visitor.walk(tree: initializer.value, handler: \.isTokenUsed) else { @@ -116,16 +122,12 @@ private extension SelfInPropertyInitializationRule { } private final class IdentifierUsageVisitor: SyntaxVisitor { - let identifier: TokenKind private(set) var isTokenUsed = false - init(identifier: TokenKind) { - self.identifier = identifier - super.init(viewMode: .sourceAccurate) - } - override func visitPost(_ node: DeclReferenceExprSyntax) { - if node.baseName.tokenKind == identifier, node.keyPathInParent != \MemberAccessExprSyntax.declName { + if node.baseName.tokenKind == .keyword(.self), + node.keyPathInParent != \MemberAccessExprSyntax.declName, + node.keyPathInParent != \KeyPathPropertyComponentSyntax.declName { isTokenUsed = true } } From e87efff39b62b1a37984d775c2f74a6eedb3f71b Mon Sep 17 00:00:00 2001 From: BlueVirusX Date: Fri, 25 Oct 2024 12:57:41 +0200 Subject: [PATCH 069/260] Support Xcode input file lists (#5790) Adds the option `--use-script-input-file-lists` and parsing for `.xcfilelist` files. --- CHANGELOG.md | 5 ++ README.md | 5 ++ .../Configuration+CommandLine.swift | 56 +++++++++++++++---- .../LintOrAnalyzeCommand.swift | 3 + Source/swiftlint/Commands/Analyze.swift | 1 + Source/swiftlint/Commands/Lint.swift | 1 + .../Common/LintOrAnalyzeArguments.swift | 2 + 7 files changed, 61 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ca68bc5cc..b87e71e2c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ [Jordan Rose](https://github.com/jrose-signal) [SimplyDanny](https://github.com/SimplyDanny) +* Support reading files to lint from Input File Lists provided + by Run Script Build Phases in Xcode using the command-line + argument `--use-script-input-file-lists`. + [BlueVirusX](https://github.com/BlueVirusX) + #### Bug Fixes * Run command plugin in whole package if no targets are defined in the diff --git a/README.md b/README.md index e5868b1cec..2d37e03326 100644 --- a/README.md +++ b/README.md @@ -514,6 +514,11 @@ can do so by passing the option `--use-script-input-files` and setting the following instance variables: `SCRIPT_INPUT_FILE_COUNT` and `SCRIPT_INPUT_FILE_0`, `SCRIPT_INPUT_FILE_1`, ..., `SCRIPT_INPUT_FILE_{SCRIPT_INPUT_FILE_COUNT - 1}`. +Similarly, files can be read from file lists by passing +the option `--use-script-input-file-lists` and setting the +following instance variables: `SCRIPT_INPUT_FILE_LIST_COUNT` +and `SCRIPT_INPUT_FILE_LIST_0`, `SCRIPT_INPUT_FILE_LIST_1`, ..., +`SCRIPT_INPUT_FILE_LIST_{SCRIPT_INPUT_FILE_LIST_COUNT - 1}`. These are same environment variables set for input files to [custom Xcode script phases](http://indiestack.com/2014/12/speeding-up-custom-script-phases/). diff --git a/Source/SwiftLintFramework/Configuration+CommandLine.swift b/Source/SwiftLintFramework/Configuration+CommandLine.swift index 6e2d1d526c..c24f5c9c55 100644 --- a/Source/SwiftLintFramework/Configuration+CommandLine.swift +++ b/Source/SwiftLintFramework/Configuration+CommandLine.swift @@ -11,16 +11,8 @@ private actor CounterActor { } } -private func scriptInputFiles() throws -> [SwiftLintFile] { - let inputFileKey = "SCRIPT_INPUT_FILE_COUNT" - guard let countString = ProcessInfo.processInfo.environment[inputFileKey] else { - throw SwiftLintError.usageError(description: "\(inputFileKey) variable not set") - } - - guard let count = Int(countString) else { - throw SwiftLintError.usageError(description: "\(inputFileKey) did not specify a number") - } - +private func readFilesFromScriptInputFiles() throws -> [SwiftLintFile] { + let count = try fileCount(from: "SCRIPT_INPUT_FILE_COUNT") return (0.. [SwiftLintFile] { } } +private func readFilesFromScriptInputFileLists() throws -> [SwiftLintFile] { + let count = try fileCount(from: "SCRIPT_INPUT_FILE_LIST_COUNT") + return (0.. Int { + guard let countString = ProcessInfo.processInfo.environment[envVar] else { + throw SwiftLintError.usageError(description: "\(envVar) variable not set") + } + guard let count = Int(countString) else { + throw SwiftLintError.usageError(description: "\(envVar) did not specify a number") + } + return count +} + #if os(Linux) private func autoreleasepool(block: () -> T) -> T { block() } #endif @@ -220,8 +250,10 @@ extension Configuration { } throw SwiftLintError.usageError(description: "stdin isn't a UTF8-encoded string") } - if options.useScriptInputFiles { - let files = try scriptInputFiles() + if options.useScriptInputFiles || options.useScriptInputFileLists { + let files = try options.useScriptInputFiles + ? readFilesFromScriptInputFiles() + : readFilesFromScriptInputFileLists() guard options.forceExclude else { return files } diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 8889fa3251..7afeef8abf 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -38,6 +38,7 @@ package struct LintOrAnalyzeOptions { let forceExclude: Bool let useExcludingByPrefix: Bool let useScriptInputFiles: Bool + let useScriptInputFileLists: Bool let benchmark: Bool let reporter: String? let baseline: String? @@ -65,6 +66,7 @@ package struct LintOrAnalyzeOptions { forceExclude: Bool, useExcludingByPrefix: Bool, useScriptInputFiles: Bool, + useScriptInputFileLists: Bool, benchmark: Bool, reporter: String?, baseline: String?, @@ -91,6 +93,7 @@ package struct LintOrAnalyzeOptions { self.forceExclude = forceExclude self.useExcludingByPrefix = useExcludingByPrefix self.useScriptInputFiles = useScriptInputFiles + self.useScriptInputFileLists = useScriptInputFileLists self.benchmark = benchmark self.reporter = reporter self.baseline = baseline diff --git a/Source/swiftlint/Commands/Analyze.swift b/Source/swiftlint/Commands/Analyze.swift index d000077baa..7ff786edec 100644 --- a/Source/swiftlint/Commands/Analyze.swift +++ b/Source/swiftlint/Commands/Analyze.swift @@ -31,6 +31,7 @@ extension SwiftLint { forceExclude: common.forceExclude, useExcludingByPrefix: common.useAlternativeExcluding, useScriptInputFiles: common.useScriptInputFiles, + useScriptInputFileLists: common.useScriptInputFileLists, benchmark: common.benchmark, reporter: common.reporter, baseline: common.baseline, diff --git a/Source/swiftlint/Commands/Lint.swift b/Source/swiftlint/Commands/Lint.swift index 27911e6fb5..eadc1ab416 100644 --- a/Source/swiftlint/Commands/Lint.swift +++ b/Source/swiftlint/Commands/Lint.swift @@ -43,6 +43,7 @@ extension SwiftLint { forceExclude: common.forceExclude, useExcludingByPrefix: common.useAlternativeExcluding, useScriptInputFiles: common.useScriptInputFiles, + useScriptInputFileLists: common.useScriptInputFileLists, benchmark: common.benchmark, reporter: common.reporter, baseline: common.baseline, diff --git a/Source/swiftlint/Common/LintOrAnalyzeArguments.swift b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift index 8b9b00eba5..2e545aa7ae 100644 --- a/Source/swiftlint/Common/LintOrAnalyzeArguments.swift +++ b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift @@ -31,6 +31,8 @@ struct LintOrAnalyzeArguments: ParsableArguments { var useAlternativeExcluding = false @Flag(help: "Read SCRIPT_INPUT_FILE* environment variables as files.") var useScriptInputFiles = false + @Flag(help: "Read SCRIPT_INPUT_FILE_LIST* environment variables as file lists.") + var useScriptInputFileLists = false @Flag(exclusivity: .exclusive) var leniency: LeniencyOptions? @Flag(help: "Exclude files in config `excluded` even if their paths are explicitly specified.") From 9ea4374145dbd48a97eacf054987e899f59f8ab4 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Fri, 25 Oct 2024 17:19:17 +0100 Subject: [PATCH 070/260] Fix `--only-rule` config issues (#5773) --- CHANGELOG.md | 9 +++ .../Extensions/Configuration+FileGraph.swift | 4 +- .../Extensions/Configuration+Parsing.swift | 20 +++--- .../Extensions/Configuration+RulesMode.swift | 47 ++++++++------ .../Configuration+RulesWrapper.swift | 50 +++++++++------ .../SwiftLintCore/Models/Configuration.swift | 12 +++- .../LintOrAnalyzeCommand.swift | 4 +- Source/swiftlint/Commands/Analyze.swift | 10 ++- Source/swiftlint/Commands/Lint.swift | 10 ++- Tests/IntegrationTests/IntegrationTests.swift | 2 +- .../CollectingRuleTests.swift | 2 +- .../CommandTests.swift | 6 +- .../ConfigurationTests+MultipleConfigs.swift | 62 +++++++++++++------ .../ConfigurationTests.swift | 29 +++++++-- .../SourceKitCrashTests.swift | 2 +- Tests/SwiftLintTestHelpers/TestHelpers.swift | 8 +-- 16 files changed, 186 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b87e71e2c9..9148f4f3dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,15 @@ [Martin Redington](https://github.com/mildm8nnered) [#5788](https://github.com/realm/SwiftLint/issues/5788) +* Fixes the `--only-rule` command line option, when a default `.swiftlint.yml` + is absent. Additionally rules specified with `--only-rule` on the command + line can now be disabled in a child configuration, to allow specific + directories to be excluded from the rule (or from being auto-corrected by + the rule), and `--only-rule` can now be specified multiple times + to run multiple rules. + [Martin Redington](https://github.com/mildm8nnered) + [#5711](https://github.com/realm/SwiftLint/issues/5711) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift b/Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift index 4c151e8ab3..464553659e 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift @@ -41,7 +41,7 @@ package extension Configuration { // MARK: - Methods internal mutating func resultingConfiguration( enableAllRules: Bool, - onlyRule: String?, + onlyRule: [String], cachePath: String? ) throws -> Configuration { // Build if needed @@ -250,7 +250,7 @@ package extension Configuration { private func merged( configurationData: [(configurationDict: [String: Any], rootDirectory: String)], enableAllRules: Bool, - onlyRule: String?, + onlyRule: [String], cachePath: String? ) throws -> Configuration { // Split into first & remainder; use empty dict for first if the array is empty diff --git a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift index 232cf0ae72..1528591840 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift @@ -42,7 +42,7 @@ extension Configuration { dict: [String: Any], ruleList: RuleList = RuleRegistry.shared.list, enableAllRules: Bool = false, - onlyRule: String? = nil, + onlyRule: [String] = [], cachePath: String? = nil ) throws { func defaultStringArray(_ object: Any?) -> [String] { [String].array(of: object) ?? [] } @@ -81,7 +81,7 @@ extension Configuration { analyzerRules: analyzerRules ) - if onlyRule == nil { + if onlyRule.isEmpty { Self.validateConfiguredRulesAreEnabled( parentConfiguration: parentConfiguration, configurationDictionary: dict, @@ -174,12 +174,12 @@ extension Configuration { } switch rulesMode { - case .allEnabled: + case .allCommandLine, .onlyCommandLine: return - case .only(let onlyRules): + case .onlyConfiguration(let onlyRules): let issue = validateConfiguredRuleIsEnabled(onlyRules: onlyRules, ruleType: ruleType) issue?.print() - case let .default(disabled: disabledRules, optIn: optInRules): + case let .defaultConfiguration(disabled: disabledRules, optIn: optInRules): let issue = validateConfiguredRuleIsEnabled( parentConfiguration: parentConfiguration, disabledRules: disabledRules, @@ -201,9 +201,11 @@ extension Configuration { var disabledInParentRules: Set = [] var allEnabledRules: Set = [] - if case .only(let onlyRules) = parentConfiguration?.rulesMode { + if case .onlyConfiguration(let onlyRules) = parentConfiguration?.rulesMode { enabledInParentRules = onlyRules - } else if case .default(let parentDisabledRules, let parentOptInRules) = parentConfiguration?.rulesMode { + } else if case .defaultConfiguration( + let parentDisabledRules, let parentOptInRules + ) = parentConfiguration?.rulesMode { enabledInParentRules = parentOptInRules disabledInParentRules = parentDisabledRules } @@ -243,7 +245,7 @@ extension Configuration { allEnabledRules: Set, ruleType: any Rule.Type ) -> Issue? { - if case .allEnabled = parentConfiguration?.rulesMode { + if case .allCommandLine = parentConfiguration?.rulesMode { if disabledRules.contains(ruleType.identifier) { return Issue.ruleDisabledInDisabledRules(ruleID: ruleType.identifier) } @@ -264,7 +266,7 @@ extension Configuration { if enabledInParentRules.union(optInRules).isDisjoint(with: allIdentifiers) { return Issue.ruleNotEnabledInOptInRules(ruleID: ruleType.identifier) } - } else if case .only(let enabledInParentRules) = parentConfiguration?.rulesMode, + } else if case .onlyConfiguration(let enabledInParentRules) = parentConfiguration?.rulesMode, enabledInParentRules.isDisjoint(with: allIdentifiers) { return Issue.ruleNotEnabledInParentOnlyRules(ruleID: ruleType.identifier) } diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift index e03cadd0a2..897fbfe39e 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift @@ -21,17 +21,21 @@ public extension Configuration { /// The default rules mode, which will enable all rules that aren't defined as being opt-in /// (conforming to the `OptInRule` protocol), minus the rules listed in `disabled`, plus the rules listed in /// `optIn`. - case `default`(disabled: Set, optIn: Set) + case defaultConfiguration(disabled: Set, optIn: Set) - /// Only enable the rules explicitly listed. - case only(Set) + /// Only enable the rules explicitly listed in the configuration files. + case onlyConfiguration(Set) + + /// Only enable the rule(s) explicitly listed on the command line (and their aliases). `--only-rule` can be + /// specified multiple times to enable multiple rules. + case onlyCommandLine(Set) /// Enable all available rules. - case allEnabled + case allCommandLine internal init( enableAllRules: Bool, - onlyRule: String?, + onlyRule: [String], onlyRules: [String], optInRules: [String], disabledRules: [String], @@ -48,9 +52,9 @@ public extension Configuration { } if enableAllRules { - self = .allEnabled - } else if let onlyRule { - self = .only(Set([onlyRule])) + self = .allCommandLine + } else if onlyRule.isNotEmpty { + self = .onlyCommandLine(Set(onlyRule)) } else if onlyRules.isNotEmpty { if disabledRules.isNotEmpty || optInRules.isNotEmpty { throw Issue.genericWarning( @@ -61,7 +65,7 @@ public extension Configuration { } warnAboutDuplicates(in: onlyRules + analyzerRules) - self = .only(Set(onlyRules + analyzerRules)) + self = .onlyConfiguration(Set(onlyRules + analyzerRules)) } else { warnAboutDuplicates(in: disabledRules) @@ -86,23 +90,28 @@ public extension Configuration { } warnAboutDuplicates(in: effectiveOptInRules + effectiveAnalyzerRules) - self = .default(disabled: Set(disabledRules), optIn: Set(effectiveOptInRules + effectiveAnalyzerRules)) + self = .defaultConfiguration( + disabled: Set(disabledRules), optIn: Set(effectiveOptInRules + effectiveAnalyzerRules) + ) } } internal func applied(aliasResolver: (String) -> String) -> Self { switch self { - case let .default(disabled, optIn): - return .default( + case let .defaultConfiguration(disabled, optIn): + return .defaultConfiguration( disabled: Set(disabled.map(aliasResolver)), optIn: Set(optIn.map(aliasResolver)) ) - case let .only(onlyRules): - return .only(Set(onlyRules.map(aliasResolver))) + case let .onlyConfiguration(onlyRules): + return .onlyConfiguration(Set(onlyRules.map(aliasResolver))) + + case let .onlyCommandLine(onlyRules): + return .onlyCommandLine(Set(onlyRules.map(aliasResolver))) - case .allEnabled: - return .allEnabled + case .allCommandLine: + return .allCommandLine } } @@ -110,9 +119,11 @@ public extension Configuration { // In the only mode, if the custom rules rule is enabled, all custom rules are also enabled implicitly // This method makes the implicitly explicit switch self { - case let .only(onlyRules) where onlyRules.contains { $0 == CustomRules.description.identifier }: + case let .onlyConfiguration(onlyRules) where onlyRules.contains { + $0 == CustomRules.identifier + }: let customRulesRule = (allRulesWrapped.first { $0.rule is CustomRules })?.rule as? CustomRules - return .only(onlyRules.union(Set(customRulesRule?.customRuleIdentifiers ?? []))) + return .onlyConfiguration(onlyRules.union(Set(customRulesRule?.customRuleIdentifiers ?? []))) default: return self diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift index e06a692a6f..2a947148a1 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift @@ -34,18 +34,18 @@ internal extension Configuration { let customRulesFilter: (RegexConfiguration) -> (Bool) var resultingRules = [any Rule]() switch mode { - case .allEnabled: + case .allCommandLine: customRulesFilter = { _ in true } resultingRules = allRulesWrapped.map(\.rule) - case var .only(onlyRulesRuleIdentifiers): + case let .onlyConfiguration(onlyRulesRuleIdentifiers), let .onlyCommandLine(onlyRulesRuleIdentifiers): customRulesFilter = { onlyRulesRuleIdentifiers.contains($0.identifier) } - onlyRulesRuleIdentifiers = validate(ruleIds: onlyRulesRuleIdentifiers, valid: validRuleIdentifiers) + let onlyRulesRuleIdentifiers = validate(ruleIds: onlyRulesRuleIdentifiers, valid: validRuleIdentifiers) resultingRules = allRulesWrapped.filter { tuple in onlyRulesRuleIdentifiers.contains(type(of: tuple.rule).description.identifier) }.map(\.rule) - case var .default(disabledRuleIdentifiers, optInRuleIdentifiers): + case var .defaultConfiguration(disabledRuleIdentifiers, optInRuleIdentifiers): customRulesFilter = { !disabledRuleIdentifiers.contains($0.identifier) } disabledRuleIdentifiers = validate(ruleIds: disabledRuleIdentifiers, valid: validRuleIdentifiers) optInRuleIdentifiers = validate(optInRuleIds: optInRuleIdentifiers, valid: validRuleIdentifiers) @@ -75,11 +75,11 @@ internal extension Configuration { lazy var disabledRuleIdentifiers: [String] = { switch mode { - case let .default(disabled, _): + case let .defaultConfiguration(disabled, _): return validate(ruleIds: disabled, valid: validRuleIdentifiers, silent: true) .sorted(by: <) - case let .only(onlyRules): + case let .onlyConfiguration(onlyRules), let .onlyCommandLine(onlyRules): return validate( ruleIds: Set(allRulesWrapped .map { type(of: $0.rule).description.identifier } @@ -88,7 +88,7 @@ internal extension Configuration { silent: true ).sorted(by: <) - case .allEnabled: + case .allCommandLine: return [] } }() @@ -147,7 +147,7 @@ internal extension Configuration { let validRuleIdentifiers = self.validRuleIdentifiers.union(child.validRuleIdentifiers) let newMode: RulesMode switch child.mode { - case let .default(childDisabled, childOptIn): + case let .defaultConfiguration(childDisabled, childOptIn): newMode = mergeDefaultMode( newAllRulesWrapped: newAllRulesWrapped, child: child, @@ -156,13 +156,21 @@ internal extension Configuration { validRuleIdentifiers: validRuleIdentifiers ) - case let .only(childOnlyRules): - // Always use the child only rules - newMode = .only(childOnlyRules) + case let .onlyConfiguration(childOnlyRules): + // Use the child only rules, unless the parent is onlyRule + switch mode { + case let .onlyCommandLine(onlyRules): + newMode = .onlyCommandLine(onlyRules) + default: + newMode = .onlyConfiguration(childOnlyRules) + } + case let .onlyCommandLine(onlyRules): + // Always use the only rule + newMode = .onlyCommandLine(onlyRules) - case .allEnabled: + case .allCommandLine: // Always use .allEnabled mode - newMode = .allEnabled + newMode = .allCommandLine } // Assemble & return merged rules @@ -225,12 +233,12 @@ internal extension Configuration { let childOptIn = child.validate(optInRuleIds: childOptIn, valid: validRuleIdentifiers) switch mode { // Switch parent's mode. Child is in default mode. - case var .default(disabled, optIn): + case var .defaultConfiguration(disabled, optIn): disabled = validate(ruleIds: disabled, valid: validRuleIdentifiers) optIn = child.validate(optInRuleIds: optIn, valid: validRuleIdentifiers) // Only use parent disabled / optIn if child config doesn't tell the opposite - return .default( + return .defaultConfiguration( disabled: Set(childDisabled).union(Set(disabled.filter { !childOptIn.contains($0) })), optIn: Set(childOptIn).union(Set(optIn.filter { !childDisabled.contains($0) })) .filter { @@ -238,7 +246,7 @@ internal extension Configuration { } ) - case var .only(onlyRules): + case var .onlyConfiguration(onlyRules): // Also add identifiers of child custom rules iff the custom_rules rule is enabled // (parent custom rules are already added) if (onlyRules.contains { $0 == CustomRules.description.identifier }) { @@ -256,13 +264,17 @@ internal extension Configuration { // Allow parent only rules that weren't disabled via the child config // & opt-ins from the child config - return .only(Set( + return .onlyConfiguration(Set( childOptIn.union(onlyRules).filter { !childDisabled.contains($0) } )) - case .allEnabled: + case let .onlyCommandLine(onlyRules): + // Like .allEnabled, rules can be disabled in a child config + return .onlyCommandLine(onlyRules.filter { !childDisabled.contains($0) }) + + case .allCommandLine: // Opt-in to every rule that isn't disabled via child config - return .default( + return .defaultConfiguration( disabled: childDisabled .filter { !isOptInRule($0, allRulesWrapped: newAllRulesWrapped) diff --git a/Source/SwiftLintCore/Models/Configuration.swift b/Source/SwiftLintCore/Models/Configuration.swift index 47401ed79e..944bbb6792 100644 --- a/Source/SwiftLintCore/Models/Configuration.swift +++ b/Source/SwiftLintCore/Models/Configuration.swift @@ -147,7 +147,7 @@ public struct Configuration { /// - parameter writeBaseline: The path to write a baseline to. /// - parameter checkForUpdates: Check for updates to SwiftLint. package init( - rulesMode: RulesMode = .default(disabled: [], optIn: []), + rulesMode: RulesMode = .defaultConfiguration(disabled: [], optIn: []), allRulesWrapped: [ConfigurationRuleWrapper]? = nil, ruleList: RuleList = RuleRegistry.shared.list, fileGraph: FileGraph? = nil, @@ -210,7 +210,7 @@ public struct Configuration { public init( configurationFiles: [String], // No default value here to avoid ambiguous Configuration() initializer enableAllRules: Bool = false, - onlyRule: String? = nil, + onlyRule: [String] = [], cachePath: String? = nil, ignoreParentAndChildConfigs: Bool = false, mockedNetworkResults: [String: String] = [:], @@ -230,7 +230,13 @@ public struct Configuration { defer { basedOnCustomConfigurationFiles = hasCustomConfigurationFiles } let currentWorkingDirectory = FileManager.default.currentDirectoryPath.bridge().absolutePathStandardized() - let rulesMode: RulesMode = enableAllRules ? .allEnabled : .default(disabled: [], optIn: []) + let rulesMode: RulesMode = if enableAllRules { + .allCommandLine + } else if onlyRule.isNotEmpty { + .onlyCommandLine(Set(onlyRule)) + } else { + .defaultConfiguration(disabled: [], optIn: []) + } // Try obtaining cached config let cacheIdentifier = "\(currentWorkingDirectory) - \(configurationFiles)" diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 7afeef8abf..f9e24151b4 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -50,7 +50,7 @@ package struct LintOrAnalyzeOptions { let cachePath: String? let ignoreCache: Bool let enableAllRules: Bool - let onlyRule: String? + let onlyRule: [String] let autocorrect: Bool let format: Bool let compilerLogPath: String? @@ -78,7 +78,7 @@ package struct LintOrAnalyzeOptions { cachePath: String?, ignoreCache: Bool, enableAllRules: Bool, - onlyRule: String?, + onlyRule: [String], autocorrect: Bool, format: Bool, compilerLogPath: String?, diff --git a/Source/swiftlint/Commands/Analyze.swift b/Source/swiftlint/Commands/Analyze.swift index 7ff786edec..10471b1dda 100644 --- a/Source/swiftlint/Commands/Analyze.swift +++ b/Source/swiftlint/Commands/Analyze.swift @@ -13,8 +13,14 @@ extension SwiftLint { var compilerLogPath: String? @Option(help: "The path of a compilation database to use when running AnalyzerRules.") var compileCommands: String? - @Option(help: "Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`.") - var onlyRule: String? + @Option( + parsing: .singleValue, + help: """ + Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`. + Can be specified repeatedly to run multiple rules. + """ + ) + var onlyRule: [String] = [] @Argument(help: pathsArgumentDescription(for: .analyze)) var paths = [String]() diff --git a/Source/swiftlint/Commands/Lint.swift b/Source/swiftlint/Commands/Lint.swift index eadc1ab416..c1edb053b7 100644 --- a/Source/swiftlint/Commands/Lint.swift +++ b/Source/swiftlint/Commands/Lint.swift @@ -19,8 +19,14 @@ extension SwiftLint { var noCache = false @Flag(help: "Run all rules, even opt-in and disabled ones, ignoring `only_rules`.") var enableAllRules = false - @Option(help: "Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`.") - var onlyRule: String? + @Option( + parsing: .singleValue, + help: """ + Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`. + Can be specified repeatedly to run multiple rules. + """ + ) + var onlyRule: [String] = [] @Argument(help: pathsArgumentDescription(for: .lint)) var paths = [String]() diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index fb6945ed47..c4830a3ed1 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -54,7 +54,7 @@ final class IntegrationTests: SwiftLintTestCase { } func testDefaultConfigurations() { - let defaultConfig = Configuration(rulesMode: .allEnabled).rules + let defaultConfig = Configuration(rulesMode: .allCommandLine).rules .map { type(of: $0) } .filter { $0.identifier != "custom_rules" } .map { diff --git a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift b/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift index 8d4aa43cb5..dc5cea9e4f 100644 --- a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift @@ -147,7 +147,7 @@ extension MockCollectingRule { RuleDescription(identifier: "test_rule", name: "", description: "", kind: .lint) } static var configuration: Configuration? { - Configuration(rulesMode: .only([description.identifier]), ruleList: RuleList(rules: self)) + Configuration(rulesMode: .onlyConfiguration([identifier]), ruleList: RuleList(rules: self)) } init(configuration _: Any) throws { self.init() } diff --git a/Tests/SwiftLintFrameworkTests/CommandTests.swift b/Tests/SwiftLintFrameworkTests/CommandTests.swift index 5532fc0fa2..6ede31b63f 100644 --- a/Tests/SwiftLintFrameworkTests/CommandTests.swift +++ b/Tests/SwiftLintFrameworkTests/CommandTests.swift @@ -393,7 +393,9 @@ final class CommandTests: SwiftLintTestCase { } func testSuperfluousDisableCommandsDisabledOnConfiguration() { - let rulesMode = Configuration.RulesMode.default(disabled: ["superfluous_disable_command"], optIn: []) + let rulesMode = Configuration.RulesMode.defaultConfiguration( + disabled: ["superfluous_disable_command"], optIn: [] + ) let configuration = Configuration(rulesMode: rulesMode) XCTAssertEqual( @@ -456,7 +458,7 @@ final class CommandTests: SwiftLintTestCase { func testSuperfluousDisableCommandsEnabledForAnalyzer() { let configuration = Configuration( - rulesMode: .default(disabled: [], optIn: [UnusedDeclarationRule.description.identifier]) + rulesMode: .defaultConfiguration(disabled: [], optIn: [UnusedDeclarationRule.identifier]) ) let violations = violations( Example(""" diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift index a5c1328741..2c3c742e66 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift @@ -52,7 +52,7 @@ extension ConfigurationTests { func testOnlyRulesMerging() { let baseConfiguration = Configuration( - rulesMode: .default( + rulesMode: .defaultConfiguration( disabled: [], optIn: [ ForceTryRule.description.identifier, @@ -60,7 +60,7 @@ extension ConfigurationTests { ] ) ) - let onlyConfiguration = Configuration(rulesMode: .only([TodoRule.description.identifier])) + let onlyConfiguration = Configuration(rulesMode: .onlyConfiguration([TodoRule.identifier])) XCTAssertTrue(baseConfiguration.contains(rule: TodoRule.self)) XCTAssertEqual(onlyConfiguration.rules.count, 1) XCTAssertTrue(onlyConfiguration.rules[0] is TodoRule) @@ -77,6 +77,25 @@ extension ConfigurationTests { XCTAssertTrue(mergedConfiguration2.contains(rule: ForceTryRule.self)) } + func testOnlyRuleMerging() { + let ruleIdentifier = TodoRule.description.identifier + let onlyRuleConfiguration = Configuration.onlyRuleConfiguration(ruleIdentifier) + + let emptyDefaultConfiguration = Configuration.emptyDefaultConfiguration() + let mergedConfiguration1 = onlyRuleConfiguration.merged(withChild: emptyDefaultConfiguration) + XCTAssertEqual(mergedConfiguration1.rules.count, 1) + XCTAssertTrue(mergedConfiguration1.rules[0] is TodoRule) + + let disabledDefaultConfiguration = Configuration.disabledDefaultConfiguration(ruleIdentifier) + let mergedConfiguration2 = onlyRuleConfiguration.merged(withChild: disabledDefaultConfiguration) + XCTAssertTrue(mergedConfiguration2.rules.isEmpty) + + let enabledOnlyConfiguration = Configuration.enabledOnlyConfiguration(ForceTryRule.description.identifier) + let mergedConfiguration3 = onlyRuleConfiguration.merged(withChild: enabledOnlyConfiguration) + XCTAssertEqual(mergedConfiguration3.rules.count, 1) + XCTAssertTrue(mergedConfiguration3.rules[0] is TodoRule) + } + func testCustomRulesMerging() { let mergedConfiguration = Mock.Config._0CustomRules.merged( withChild: Mock.Config._2CustomRules, @@ -345,11 +364,11 @@ extension ConfigurationTests { XCTAssertTrue((ruleType as Any) is any OptInRule.Type) let ruleIdentifier = ruleType.description.identifier for testCase in testCases { - let parentConfiguration = Configuration(rulesMode: .default( + let parentConfiguration = Configuration(rulesMode: .defaultConfiguration( disabled: testCase.disabledInParent ? [ruleIdentifier] : [], optIn: testCase.optedInInParent ? [ruleIdentifier] : [] )) - let childConfiguration = Configuration(rulesMode: .default( + let childConfiguration = Configuration(rulesMode: .defaultConfiguration( disabled: testCase.disabledInChild ? [ruleIdentifier] : [], optIn: testCase.optedInInChild ? [ruleIdentifier] : [] )) @@ -380,10 +399,10 @@ extension ConfigurationTests { let ruleIdentifier = ruleType.description.identifier for testCase in testCases { let parentConfiguration = Configuration( - rulesMode: .default(disabled: testCase.disabledInParent ? [ruleIdentifier] : [], optIn: []) + rulesMode: .defaultConfiguration(disabled: testCase.disabledInParent ? [ruleIdentifier] : [], optIn: []) ) let childConfiguration = Configuration( - rulesMode: .default(disabled: testCase.disabledInChild ? [ruleIdentifier] : [], optIn: []) + rulesMode: .defaultConfiguration(disabled: testCase.disabledInChild ? [ruleIdentifier] : [], optIn: []) ) let mergedConfiguration = parentConfiguration.merged(withChild: childConfiguration) let isEnabled = mergedConfiguration.contains(rule: ruleType) @@ -410,9 +429,9 @@ extension ConfigurationTests { let ruleType = ImplicitReturnRule.self XCTAssertTrue((ruleType as Any) is any OptInRule.Type) let ruleIdentifier = ruleType.description.identifier - let parentConfiguration = Configuration(rulesMode: .only([ruleIdentifier])) + let parentConfiguration = Configuration(rulesMode: .onlyConfiguration([ruleIdentifier])) for testCase in testCases { - let childConfiguration = Configuration(rulesMode: .default( + let childConfiguration = Configuration(rulesMode: .defaultConfiguration( disabled: testCase.disabledInChild ? [ruleIdentifier] : [], optIn: testCase.optedInInChild ? [ruleIdentifier] : [] )) @@ -448,10 +467,10 @@ extension ConfigurationTests { ] let configurations = [ - Configuration(rulesMode: .default(disabled: [], optIn: [])), - Configuration(rulesMode: .default(disabled: [], optIn: [ruleIdentifier])), - Configuration(rulesMode: .default(disabled: [ruleIdentifier], optIn: [ruleIdentifier])), - Configuration(rulesMode: .default(disabled: [ruleIdentifier], optIn: [])), + Configuration(rulesMode: .defaultConfiguration(disabled: [], optIn: [])), + Configuration(rulesMode: .defaultConfiguration(disabled: [], optIn: [ruleIdentifier])), + Configuration(rulesMode: .defaultConfiguration(disabled: [ruleIdentifier], optIn: [ruleIdentifier])), + Configuration(rulesMode: .defaultConfiguration(disabled: [ruleIdentifier], optIn: [])), ] for parentConfiguration in parentConfigurations { @@ -466,7 +485,7 @@ extension ConfigurationTests { configuration: Configuration, ruleType: any Rule.Type ) { - guard case .default(let disabledRules, let optInRules) = configuration.rulesMode else { + guard case .defaultConfiguration(let disabledRules, let optInRules) = configuration.rulesMode else { XCTFail("Configuration rulesMode was not the default") return } @@ -637,20 +656,23 @@ extension ConfigurationTests { private extension Configuration { static func emptyDefaultConfiguration() -> Self { - Configuration(rulesMode: .default(disabled: [], optIn: [])) + Configuration(rulesMode: .defaultConfiguration(disabled: [], optIn: [])) } static func optInDefaultConfiguration(_ ruleIdentifier: String) -> Self { - Configuration(rulesMode: .default(disabled: [], optIn: [ruleIdentifier])) + Configuration(rulesMode: .defaultConfiguration(disabled: [], optIn: [ruleIdentifier])) } static func optInDisabledDefaultConfiguration(_ ruleIdentifier: String) -> Self { - Configuration(rulesMode: .default(disabled: [ruleIdentifier], optIn: [ruleIdentifier])) + Configuration(rulesMode: .defaultConfiguration(disabled: [ruleIdentifier], optIn: [ruleIdentifier])) } static func disabledDefaultConfiguration(_ ruleIdentifier: String) -> Self { - Configuration(rulesMode: .default(disabled: [ruleIdentifier], optIn: [])) + Configuration(rulesMode: .defaultConfiguration(disabled: [ruleIdentifier], optIn: [])) } - static func emptyOnlyConfiguration() -> Self { Configuration(rulesMode: .only([])) } + static func emptyOnlyConfiguration() -> Self { Configuration(rulesMode: .onlyConfiguration([])) } static func enabledOnlyConfiguration(_ ruleIdentifier: String) -> Self { - Configuration(rulesMode: .only([ruleIdentifier])) + Configuration(rulesMode: .onlyConfiguration([ruleIdentifier])) + } + static func allEnabledConfiguration() -> Self { Configuration(rulesMode: .allCommandLine)} + static func onlyRuleConfiguration(_ ruleIdentifier: String) -> Self { + Configuration(rulesMode: .onlyCommandLine([ruleIdentifier])) } - static func allEnabledConfiguration() -> Self { Configuration(rulesMode: .allEnabled)} } diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index f0ea288ba0..f718aed784 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -90,13 +90,27 @@ final class ConfigurationTests: SwiftLintTestCase { func testOnlyRule() throws { let configuration = try Configuration( dict: [:], - onlyRule: "nesting", + onlyRule: ["nesting"], cachePath: nil ) XCTAssertEqual(configuration.rules.count, 1) } + func testOnlyRuleMultiple() throws { + let onlyRuleIdentifiers = ["nesting", "todo"].sorted() + let configuration = try Configuration( + dict: ["only_rules": "line_length"], + onlyRule: onlyRuleIdentifiers, + cachePath: nil + ) + XCTAssertEqual(onlyRuleIdentifiers, configuration.enabledRuleIdentifiers) + + let childConfiguration = try Configuration(dict: ["disabled_rules": onlyRuleIdentifiers.last ?? ""]) + let mergedConfiguration = configuration.merged(withChild: childConfiguration) + XCTAssertEqual(onlyRuleIdentifiers.dropLast(), mergedConfiguration.enabledRuleIdentifiers) + } + func testOnlyRules() throws { let only = ["nesting", "todo"] @@ -180,10 +194,7 @@ final class ConfigurationTests: SwiftLintTestCase { "initializing Configuration with valid rules in YAML string should succeed") let expectedIdentifiers = Set(RuleRegistry.shared.list.list.keys .filter({ !([validRule] + optInRules).contains($0) })) - let configuredIdentifiers = Set(configuration.rules.map { - type(of: $0).description.identifier - }) - XCTAssertEqual(expectedIdentifiers, configuredIdentifiers) + XCTAssertEqual(expectedIdentifiers, Set(configuration.enabledRuleIdentifiers)) } func testDuplicatedRules() { @@ -592,3 +603,11 @@ private extension Sequence where Element == String { map { $0.absolutePathStandardized() } } } + +private extension Configuration { + var enabledRuleIdentifiers: [String] { + rules.map { + type(of: $0).identifier + }.sorted() + } +} diff --git a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift b/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift index dab162bfaf..4b5b8d3e22 100644 --- a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift +++ b/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift @@ -52,7 +52,7 @@ final class SourceKitCrashTests: SwiftLintTestCase { file.assertHandler = { XCTFail("If this called, rule's SourceKitFreeRule is not properly configured") } - let configuration = Configuration(rulesMode: .only(allRuleIdentifiers)) + let configuration = Configuration(rulesMode: .onlyConfiguration(allRuleIdentifiers)) let storage = RuleStorage() _ = Linter(file: file, configuration: configuration).collect(into: storage).styleViolations(using: storage) file.sourcekitdFailed = false diff --git a/Tests/SwiftLintTestHelpers/TestHelpers.swift b/Tests/SwiftLintTestHelpers/TestHelpers.swift index 6a3293133f..c28f43e055 100644 --- a/Tests/SwiftLintTestHelpers/TestHelpers.swift +++ b/Tests/SwiftLintTestHelpers/TestHelpers.swift @@ -52,7 +52,7 @@ public let allRuleIdentifiers = Set(RuleRegistry.shared.list.list.keys) public extension Configuration { func applyingConfiguration(from example: Example) -> Configuration { guard let exampleConfiguration = example.configuration, - case let .only(onlyRules) = self.rulesMode, + case let .onlyConfiguration(onlyRules) = self.rulesMode, let firstRule = (onlyRules.first { $0 != "superfluous_disable_command" }), case let configDict: [_: any Sendable] = ["only_rules": onlyRules, firstRule: exampleConfiguration], let typedConfiguration = try? Configuration(dict: configDict) else { return self } @@ -281,12 +281,12 @@ public func makeConfig(_ ruleConfiguration: Any?, return (try? ruleType.init(configuration: ruleConfiguration)).flatMap { configuredRule in let rules = skipDisableCommandTests ? [configuredRule] : [configuredRule, SuperfluousDisableCommandRule()] return Configuration( - rulesMode: .only(identifiers), + rulesMode: .onlyConfiguration(identifiers), allRulesWrapped: rules.map { ($0, false) } ) } } - return Configuration(rulesMode: .only(identifiers)) + return Configuration(rulesMode: .onlyConfiguration(identifiers)) } private func testCorrection(_ correction: (Example, Example), @@ -299,7 +299,7 @@ private func testCorrection(_ correction: (Example, Example), #endif var config = configuration if let correctionConfiguration = correction.0.configuration, - case let .only(onlyRules) = configuration.rulesMode, + case let .onlyConfiguration(onlyRules) = configuration.rulesMode, let ruleToConfigure = (onlyRules.first { $0 != SuperfluousDisableCommandRule.description.identifier }), case let configDict: [_: any Sendable] = ["only_rules": onlyRules, ruleToConfigure: correctionConfiguration], let typedConfiguration = try? Configuration(dict: configDict) { From 832821259a499553f8e2056a13697d89b6c10b38 Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Fri, 25 Oct 2024 20:43:49 +0300 Subject: [PATCH 071/260] Make use of transitive imports configuration (#5622) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 6 +++ .../Rules/Lint/UnusedImportRule.swift | 34 +++++++++++----- .../Rules/Lint/UnusedImportRuleExamples.swift | 40 +++++++++++++++++-- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9148f4f3dd..b7f2100741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,12 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5835](https://github.com/realm/SwiftLint/issues/5835) +* Allow to specify transitive modules to be taken into account by + `unused_import` rule. This avoids that required imports are removed. + [Paul Taykalo](https://github.com/PaulTaykalo) + [SimplyDanny](https://github.com/SimplyDanny) + [#5167](https://github.com/realm/SwiftLint/issues/5167) + * Do not throw deprecation warning if deprecated property is not presented in configuration. [chipp](https://github.com/chipp) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift index e61203ef3a..175a6cb40c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift @@ -118,17 +118,11 @@ private extension SwiftLintFile { ) } - let unusedImportUsages = rangedAndSortedUnusedImports(of: Array(unusedImports)) - .map { ImportUsage.unused(module: $0, range: $1) } - - guard configuration.requireExplicitImports else { - return unusedImportUsages - } - + // Find the missing imports, which should be imported, but are not. let currentModule = (compilerArguments.firstIndex(of: "-module-name")?.advanced(by: 1)) .map { compilerArguments[$0] } - let missingImports = usrFragments + var missingImports = usrFragments .subtracting(imports + [currentModule].compactMap({ $0 })) .filter { module in let modulesAllowedToImportCurrentModule = configuration.allowedTransitiveImports @@ -140,7 +134,29 @@ private extension SwiftLintFile { imports.isDisjoint(with: modulesAllowedToImportCurrentModule) } - return unusedImportUsages + missingImports.sorted().map { .missing(module: $0) } + // Check if unused imports were used for transitive imports + var foundUmbrellaModules = Set() + var foundMissingImports = Set() + for missingImport in missingImports { + let umbrellaModules = configuration.allowedTransitiveImports + .filter { $0.transitivelyImportedModules.contains(missingImport) } + .map(\.importedModule) + if umbrellaModules.isEmpty { + continue + } + foundMissingImports.insert(missingImport) + foundUmbrellaModules.formUnion(umbrellaModules.filter(unusedImports.contains)) + } + + unusedImports.subtract(foundUmbrellaModules) + missingImports.subtract(foundMissingImports) + + let unusedImportUsages = rangedAndSortedUnusedImports(of: Array(unusedImports)) + .map { ImportUsage.unused(module: $0, range: $1) } + + return configuration.requireExplicitImports + ? unusedImportUsages + missingImports.sorted().map { .missing(module: $0) } + : unusedImportUsages } func getImportsAndUSRFragments(compilerArguments: [String]) -> (imports: Set, usrFragments: Set) { diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRuleExamples.swift index 5a74494073..fdd33285e1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRuleExamples.swift @@ -25,6 +25,33 @@ struct UnusedImportRuleExamples { let 👨‍👩‍👧‍👦 = #selector(NSArray.contains(_:)) 👨‍👩‍👧‍👦 == 👨‍👩‍👧‍👦 """), + Example(""" + import Foundation + enum E { + static let min: CGFloat = 44 + } + """, configuration: [ + "allowed_transitive_imports": [ + [ + "module": "Foundation", + "allowed_transitive_imports": ["CoreFoundation"], + ] as [String: any Sendable], + ], + ]), + Example(""" + import SwiftUI + + final class EditMode: ObservableObject { + @Published var isEditing = false + } + """, configuration: [ + "allowed_transitive_imports": [ + [ + "module": "SwiftUI", + "allowed_transitive_imports": ["Foundation"], + ] as [String: any Sendable], + ], + ], excludeFromDocumentation: true), ] static let triggeringExamples = [ @@ -152,30 +179,35 @@ struct UnusedImportRuleExamples { class A {} """), Example(""" - ↓↓import Foundation + import Foundation typealias Foo = CFArray + dispatchMain() """, configuration: [ "require_explicit_imports": true, "allowed_transitive_imports": [ [ "module": "Foundation", - "allowed_transitive_imports": ["CoreFoundation"], + "allowed_transitive_imports": ["CoreFoundation", "Dispatch"], ] as [String: any Sendable], ], ] as [String: any Sendable], testMultiByteOffsets: false, testOnLinux: false): Example(""" - import CoreFoundation + import Foundation typealias Foo = CFArray + dispatchMain() """), Example(""" - ↓↓import Foundation + ↓↓↓import Foundation typealias Foo = CFData + dispatchMain() """, configuration: [ "require_explicit_imports": true ], testMultiByteOffsets: false, testOnLinux: false): Example(""" import CoreFoundation + import Dispatch typealias Foo = CFData + dispatchMain() """), Example(""" import Foundation From 01f5ecd64a696468f90a1489f8ab1ac9adf0d6ff Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Sat, 26 Oct 2024 14:46:03 +0100 Subject: [PATCH 072/260] Replace `description.identifier` with `identifier` (#5837) --- .../Rules/Lint/CaptureVariableRule.swift | 2 +- .../Rules/Lint/InertDeferRule.swift | 2 +- .../Rules/Lint/TypesafeArrayInitRule.swift | 2 +- .../Rules/Lint/UnusedCaptureListRule.swift | 2 +- .../Rules/Lint/UnusedDeclarationRule.swift | 6 +++--- .../Rules/Lint/UnusedImportRule.swift | 8 ++++---- .../Rules/Style/ExplicitSelfRule.swift | 4 ++-- .../Documentation/RuleDocumentation.swift | 6 +++--- .../Extensions/Configuration+Cache.swift | 2 +- .../Extensions/Configuration+Parsing.swift | 2 +- .../Extensions/Configuration+RulesMode.swift | 2 +- .../Configuration+RulesWrapper.swift | 16 ++++++++-------- .../ChildOptionSeverityConfiguration.swift | 2 +- .../SwiftLintCore/Models/Configuration.swift | 4 ++-- ...shableConfigurationRuleWrapperWrapper.swift | 6 +++--- Source/SwiftLintCore/Models/Linter.swift | 6 +++--- Source/SwiftLintCore/Models/RuleList.swift | 2 +- .../RegexConfiguration.swift | 6 +++--- Source/SwiftLintCore/Rules/CustomRules.swift | 6 +++--- Source/SwiftLintFramework/RulesFilter.swift | 2 +- Tests/CLITests/RulesFilterTests.swift | 10 +++++----- .../CompilerProtocolInitRuleTests.swift | 2 +- .../ComputedAccessorsOrderRuleTests.swift | 2 +- .../ConfigurationAliasesTests.swift | 2 +- .../ConfigurationTests+MultipleConfigs.swift | 18 +++++++++--------- .../ConfigurationTests.swift | 10 +++++----- .../ContainsOverFirstNotNilRuleTests.swift | 2 +- .../CustomRulesTests.swift | 6 +++--- ...yclomaticComplexityConfigurationTests.swift | 2 +- .../DeploymentTargetConfigurationTests.swift | 2 +- .../DeploymentTargetRuleTests.swift | 2 +- .../ExpiringTodoRuleTests.swift | 2 +- ...plicitTypeInterfaceConfigurationTests.swift | 4 ++-- .../FunctionBodyLengthRuleTests.swift | 2 +- .../ImplicitGetterRuleTests.swift | 4 ++-- .../ImplicitReturnConfigurationTests.swift | 2 +- ...lyUnwrappedOptionalConfigurationTests.swift | 2 +- .../IndentationWidthRuleTests.swift | 2 +- .../LineLengthConfigurationTests.swift | 4 ++-- .../ModifierOrderTests.swift | 2 +- .../NameConfigurationTests.swift | 2 +- .../NestingRuleTests.swift | 4 ++-- .../NoEmptyBlockConfigurationTests.swift | 4 ++-- .../RuleConfigurationTests.swift | 10 +++++----- .../TodoRuleTests.swift | 2 +- .../TrailingCommaRuleTests.swift | 2 +- .../VerticalWhitespaceRuleTests.swift | 2 +- .../XCTSpecificMatcherRuleTests.swift | 2 +- Tests/SwiftLintTestHelpers/TestHelpers.swift | 6 +++--- 49 files changed, 102 insertions(+), 102 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/CaptureVariableRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/CaptureVariableRule.swift index 10c4d6b20a..bbf8d915ff 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/CaptureVariableRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/CaptureVariableRule.swift @@ -264,7 +264,7 @@ private extension SwiftLintFile { let path = self.path, let response = try? Request.index(file: path, arguments: compilerArguments).sendIfNotDisabled() else { - Issue.indexingError(path: path, ruleID: CaptureVariableRule.description.identifier).print() + Issue.indexingError(path: path, ruleID: CaptureVariableRule.identifier).print() return nil } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift index abecbf32c6..87a8775ebd 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift @@ -2,7 +2,7 @@ import SwiftSyntax // TODO: [12/23/2024] Remove deprecation warning after ~2 years. private let warnDeprecatedOnceImpl: Void = { - Issue.ruleDeprecated(ruleID: InertDeferRule.description.identifier).print() + Issue.ruleDeprecated(ruleID: InertDeferRule.identifier).print() }() private func warnDeprecatedOnce() { diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift index faa619e479..fb353a8ac6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/TypesafeArrayInitRule.swift @@ -67,7 +67,7 @@ struct TypesafeArrayInitRule: AnalyzerRule { return [] } guard compilerArguments.isNotEmpty else { - Issue.missingCompilerArguments(path: file.path, ruleID: Self.description.identifier).print() + Issue.missingCompilerArguments(path: file.path, ruleID: Self.identifier).print() return [] } return Self.parentRule.validate(file: file) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift index d8ce4a85be..445aba6edf 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift @@ -2,7 +2,7 @@ import SwiftSyntax // TODO: [12/22/2024] Remove deprecation warning after ~2 years. private let warnDeprecatedOnceImpl: Void = { - Issue.ruleDeprecated(ruleID: UnusedCaptureListRule.description.identifier).print() + Issue.ruleDeprecated(ruleID: UnusedCaptureListRule.identifier).print() }() private func warnDeprecatedOnce() { diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift index d851ab72c0..33e3d23556 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift @@ -30,18 +30,18 @@ struct UnusedDeclarationRule: AnalyzerRule, CollectingRule { func collectInfo(for file: SwiftLintFile, compilerArguments: [String]) -> Self.FileUSRs { guard compilerArguments.isNotEmpty else { - Issue.missingCompilerArguments(path: file.path, ruleID: Self.description.identifier).print() + Issue.missingCompilerArguments(path: file.path, ruleID: Self.identifier).print() return .empty } guard let index = file.index(compilerArguments: compilerArguments), index.value.isNotEmpty else { - Issue.indexingError(path: file.path, ruleID: Self.description.identifier).print() + Issue.indexingError(path: file.path, ruleID: Self.identifier).print() return .empty } guard let editorOpen = (try? Request.editorOpen(file: file.file).sendIfNotDisabled()) .map(SourceKittenDictionary.init) else { - Issue.fileNotReadable(path: file.path, ruleID: Self.description.identifier).print() + Issue.fileNotReadable(path: file.path, ruleID: Self.identifier).print() return .empty } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift index 175a6cb40c..6e1951a482 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift @@ -84,7 +84,7 @@ struct UnusedImportRule: CorrectableRule, AnalyzerRule { private func importUsage(in file: SwiftLintFile, compilerArguments: [String]) -> [ImportUsage] { guard compilerArguments.isNotEmpty else { - Issue.missingCompilerArguments(path: file.path, ruleID: Self.description.identifier).print() + Issue.missingCompilerArguments(path: file.path, ruleID: Self.identifier).print() return [] } @@ -178,7 +178,7 @@ private extension SwiftLintFile { file: path!, offset: token.offset, arguments: compilerArguments ) guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else { - Issue.missingCursorInfo(path: path, ruleID: UnusedImportRule.description.identifier).print() + Issue.missingCursorInfo(path: path, ruleID: UnusedImportRule.identifier).print() continue } if nextIsModuleImport { @@ -213,7 +213,7 @@ private extension SwiftLintFile { func operatorImports(arguments: [String], processedTokenOffsets: Set) -> Set { guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled()) .map(SourceKittenDictionary.init) else { - Issue.indexingError(path: path, ruleID: UnusedImportRule.description.identifier).print() + Issue.indexingError(path: path, ruleID: UnusedImportRule.identifier).print() return [] } @@ -236,7 +236,7 @@ private extension SwiftLintFile { ) guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()) .map(SourceKittenDictionary.init) else { - Issue.missingCursorInfo(path: path, ruleID: UnusedImportRule.description.identifier).print() + Issue.missingCursorInfo(path: path, ruleID: UnusedImportRule.identifier).print() continue } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift index 5aa83a05ed..7d86574842 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift @@ -42,7 +42,7 @@ struct ExplicitSelfRule: CorrectableRule, AnalyzerRule { private func violationRanges(in file: SwiftLintFile, compilerArguments: [String]) -> [NSRange] { guard compilerArguments.isNotEmpty else { - Issue.missingCompilerArguments(path: file.path, ruleID: Self.description.identifier).print() + Issue.missingCompilerArguments(path: file.path, ruleID: Self.identifier).print() return [] } @@ -69,7 +69,7 @@ struct ExplicitSelfRule: CorrectableRule, AnalyzerRule { return cursorsMissingExplicitSelf.compactMap { cursorInfo in guard let byteOffset = (cursorInfo["swiftlint.offset"] as? Int64).flatMap(ByteCount.init) else { - Issue.genericWarning("Cannot convert offsets in '\(Self.description.identifier)' rule.").print() + Issue.genericWarning("Cannot convert offsets in '\(Self.identifier)' rule.").print() return nil } diff --git a/Source/SwiftLintCore/Documentation/RuleDocumentation.swift b/Source/SwiftLintCore/Documentation/RuleDocumentation.swift index c47a5d8383..00dd8f35e8 100644 --- a/Source/SwiftLintCore/Documentation/RuleDocumentation.swift +++ b/Source/SwiftLintCore/Documentation/RuleDocumentation.swift @@ -31,10 +31,10 @@ struct RuleDocumentation { var ruleName: String { ruleType.description.name } /// The identifier of the documented rule. - var ruleIdentifier: String { ruleType.description.identifier } + var ruleIdentifier: String { ruleType.identifier } /// The name of the file on disk for this rule documentation. - var fileName: String { "\(ruleType.description.identifier).md" } + var fileName: String { "\(ruleType.identifier).md" } /// The contents of the file for this rule documentation. var fileContents: String { @@ -81,7 +81,7 @@ private func h2(_ text: String) -> String { "## \(text)" } private func detailsSummary(_ rule: some Rule) -> String { let ruleDescription = """ - * **Identifier:** `\(type(of: rule).description.identifier)` + * **Identifier:** `\(type(of: rule).identifier)` * **Enabled by default:** \(rule is any OptInRule ? "No" : "Yes") * **Supports autocorrection:** \(rule is any CorrectableRule ? "Yes" : "No") * **Kind:** \(type(of: rule).description.kind) diff --git a/Source/SwiftLintCore/Extensions/Configuration+Cache.swift b/Source/SwiftLintCore/Extensions/Configuration+Cache.swift index 56debce0ce..c3e575a8b4 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Cache.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Cache.swift @@ -60,7 +60,7 @@ extension Configuration { } let cacheRulesDescriptions = rules - .map { rule in [type(of: rule).description.identifier, rule.cacheDescription] } + .map { rule in [type(of: rule).identifier, rule.cacheDescription] } .sorted { $0[0] < $1[0] } let jsonObject: [Any] = [rootDirectory, cacheRulesDescriptions] if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject) { diff --git a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift index 1528591840..54dcf399da 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift @@ -66,7 +66,7 @@ extension Configuration { allRulesWrapped = try ruleList.allRulesWrapped(configurationDict: dict) } catch let RuleListError.duplicatedConfigurations(ruleType) { let aliases = ruleType.description.deprecatedAliases.map { "'\($0)'" }.joined(separator: ", ") - let identifier = ruleType.description.identifier + let identifier = ruleType.identifier throw Issue.genericWarning( "Multiple configurations found for '\(identifier)'. Check for any aliases: \(aliases)." ) diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift index 897fbfe39e..b5540cf3ad 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift @@ -6,7 +6,7 @@ public extension Configuration { /// - returns: The rule for the specified ID, if configured in this configuration. func configuredRule(forID ruleID: String) -> (any Rule)? { rules.first { rule in - if type(of: rule).description.identifier == ruleID { + if type(of: rule).identifier == ruleID { if let customRules = rule as? CustomRules { return customRules.configuration.customRuleConfigurations.isNotEmpty } diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift index 2a947148a1..6e3295d951 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift @@ -11,7 +11,7 @@ internal extension Configuration { private var invalidRuleIdsWarnedAbout: Set = [] private var validRuleIdentifiers: Set { - let regularRuleIdentifiers = allRulesWrapped.map { type(of: $0.rule).description.identifier } + let regularRuleIdentifiers = allRulesWrapped.map { type(of: $0.rule).identifier } let configurationCustomRulesIdentifiers = (allRulesWrapped.first { $0.rule is CustomRules }?.rule as? CustomRules)?.customRuleIdentifiers ?? [] return Set(regularRuleIdentifiers + configurationCustomRulesIdentifiers) @@ -42,7 +42,7 @@ internal extension Configuration { customRulesFilter = { onlyRulesRuleIdentifiers.contains($0.identifier) } let onlyRulesRuleIdentifiers = validate(ruleIds: onlyRulesRuleIdentifiers, valid: validRuleIdentifiers) resultingRules = allRulesWrapped.filter { tuple in - onlyRulesRuleIdentifiers.contains(type(of: tuple.rule).description.identifier) + onlyRulesRuleIdentifiers.contains(type(of: tuple.rule).identifier) }.map(\.rule) case var .defaultConfiguration(disabledRuleIdentifiers, optInRuleIdentifiers): @@ -50,7 +50,7 @@ internal extension Configuration { disabledRuleIdentifiers = validate(ruleIds: disabledRuleIdentifiers, valid: validRuleIdentifiers) optInRuleIdentifiers = validate(optInRuleIds: optInRuleIdentifiers, valid: validRuleIdentifiers) resultingRules = allRulesWrapped.filter { tuple in - let id = type(of: tuple.rule).description.identifier + let id = type(of: tuple.rule).identifier return !disabledRuleIdentifiers.contains(id) && (!(tuple.rule is any OptInRule) || optInRuleIdentifiers.contains(id)) }.map(\.rule) @@ -65,7 +65,7 @@ internal extension Configuration { // Sort by name resultingRules = resultingRules.sorted { - type(of: $0).description.identifier < type(of: $1).description.identifier + type(of: $0).identifier < type(of: $1).identifier } // Store & return @@ -82,7 +82,7 @@ internal extension Configuration { case let .onlyConfiguration(onlyRules), let .onlyCommandLine(onlyRules): return validate( ruleIds: Set(allRulesWrapped - .map { type(of: $0.rule).description.identifier } + .map { type(of: $0.rule).identifier } .filter { !onlyRules.contains($0) }), valid: validRuleIdentifiers, silent: true @@ -249,7 +249,7 @@ internal extension Configuration { case var .onlyConfiguration(onlyRules): // Also add identifiers of child custom rules iff the custom_rules rule is enabled // (parent custom rules are already added) - if (onlyRules.contains { $0 == CustomRules.description.identifier }) { + if (onlyRules.contains { $0 == CustomRules.identifier }) { if let childCustomRulesRule = (child.allRulesWrapped.first { $0.rule is CustomRules })?.rule as? CustomRules { onlyRules = onlyRules.union( @@ -279,7 +279,7 @@ internal extension Configuration { .filter { !isOptInRule($0, allRulesWrapped: newAllRulesWrapped) }, - optIn: Set(newAllRulesWrapped.map { type(of: $0.rule).description.identifier } + optIn: Set(newAllRulesWrapped.map { type(of: $0.rule).identifier } .filter { !childDisabled.contains($0) && isOptInRule($0, allRulesWrapped: newAllRulesWrapped) @@ -298,7 +298,7 @@ internal extension Configuration { } let isOptInRule = allRulesWrapped - .first { type(of: $0.rule).description.identifier == identifier }?.rule is any OptInRule + .first { type(of: $0.rule).identifier == identifier }?.rule is any OptInRule Self.isOptInRuleCache[identifier] = isOptInRule return isOptInRule } diff --git a/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift b/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift index fb4aed94b8..4f63881029 100644 --- a/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift +++ b/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift @@ -23,7 +23,7 @@ public struct ChildOptionSeverityConfiguration: RuleConfiguration, public mutating func apply(configuration: Any) throws { guard let configString = configuration as? String, let optionSeverity = ChildOptionSeverity(rawValue: configString.lowercased()) else { - throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + throw Issue.invalidConfiguration(ruleID: Parent.identifier) } self.optionSeverity = optionSeverity } diff --git a/Source/SwiftLintCore/Models/Configuration.swift b/Source/SwiftLintCore/Models/Configuration.swift index 944bbb6792..b8169afe11 100644 --- a/Source/SwiftLintCore/Models/Configuration.swift +++ b/Source/SwiftLintCore/Models/Configuration.swift @@ -309,7 +309,7 @@ extension Configuration: Hashable { hasher.combine(checkForUpdates) hasher.combine(basedOnCustomConfigurationFiles) hasher.combine(cachePath) - hasher.combine(rules.map { type(of: $0).description.identifier }) + hasher.combine(rules.map { type(of: $0).identifier }) hasher.combine(fileGraph) } @@ -344,6 +344,6 @@ extension Configuration: CustomStringConvertible { + "- Reporter: \(reporter ?? "default")\n" + "- Cache Path: \(cachePath as Optional)\n" + "- Computed Cache Description: \(computedCacheDescription as Optional)\n" - + "- Rules: \(rules.map { type(of: $0).description.identifier })" + + "- Rules: \(rules.map { type(of: $0).identifier })" } } diff --git a/Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift b/Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift index 05ff17312e..1abe3deae2 100644 --- a/Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift +++ b/Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift @@ -5,11 +5,11 @@ internal struct HashableConfigurationRuleWrapperWrapper: Hashable { lhs: Self, rhs: Self ) -> Bool { // Only use identifier for equality check (not taking config into account) - type(of: lhs.configurationRuleWrapper.rule).description.identifier - == type(of: rhs.configurationRuleWrapper.rule).description.identifier + type(of: lhs.configurationRuleWrapper.rule).identifier + == type(of: rhs.configurationRuleWrapper.rule).identifier } func hash(into hasher: inout Hasher) { - hasher.combine(type(of: configurationRuleWrapper.rule).description.identifier) + hasher.combine(type(of: configurationRuleWrapper.rule).identifier) } } diff --git a/Source/SwiftLintCore/Models/Linter.swift b/Source/SwiftLintCore/Models/Linter.swift index c87065a557..68f111fdc9 100644 --- a/Source/SwiftLintCore/Models/Linter.swift +++ b/Source/SwiftLintCore/Models/Linter.swift @@ -89,7 +89,7 @@ private extension Rule { return nil } - let ruleID = Self.description.identifier + let ruleID = Self.identifier let violations: [StyleViolation] let ruleTime: (String, Double)? @@ -351,7 +351,7 @@ public struct CollectedLinter { let totalTime = -start.timeIntervalSinceNow let fractionedTime = totalTime / TimeInterval(rules.count) ruleTimes = rules.compactMap { rule in - let id = type(of: rule).description.identifier + let id = type(of: rule).identifier return (id, fractionedTime) } } @@ -414,7 +414,7 @@ public struct CollectedLinter { .configuration.customRuleConfigurations.map { RuleIdentifier($0.identifier) } ?? [] let allRuleIdentifiers = RuleRegistry.shared.list.allValidIdentifiers().map { RuleIdentifier($0) } let allValidIdentifiers = Set(allCustomIdentifiers + allRuleIdentifiers + [.all]) - let superfluousRuleIdentifier = RuleIdentifier(SuperfluousDisableCommandRule.description.identifier) + let superfluousRuleIdentifier = RuleIdentifier(SuperfluousDisableCommandRule.identifier) return regions.flatMap { region in region.disabledRuleIdentifiers.filter({ diff --git a/Source/SwiftLintCore/Models/RuleList.swift b/Source/SwiftLintCore/Models/RuleList.swift index e8dd55b5c8..79dab75ac6 100644 --- a/Source/SwiftLintCore/Models/RuleList.swift +++ b/Source/SwiftLintCore/Models/RuleList.swift @@ -27,7 +27,7 @@ public struct RuleList { var tmpAliases = [String: String]() for rule in rules { - let identifier = rule.description.identifier + let identifier = rule.identifier tmpList[identifier] = rule for alias in rule.description.deprecatedAliases { tmpAliases[alias] = identifier diff --git a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift index 65b44876f6..c4d3acf106 100644 --- a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift +++ b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift @@ -60,7 +60,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, public mutating func apply(configuration: Any) throws { guard let configurationDict = configuration as? [String: Any], let regexString = configurationDict[$regex.key] as? String else { - throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + throw Issue.invalidConfiguration(ruleID: Parent.identifier) } regex = try RegularExpression(pattern: regexString) @@ -92,7 +92,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, } if let captureGroup = configurationDict["capture_group"] as? Int { guard (0 ... regex.numberOfCaptureGroups).contains(captureGroup) else { - throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + throw Issue.invalidConfiguration(ruleID: Parent.identifier) } self.captureGroup = captureGroup } @@ -142,7 +142,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, if let kind = SyntaxKind(shortName: $0) { return kind } - throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + throw Issue.invalidConfiguration(ruleID: Parent.identifier) } return Set(kinds) } diff --git a/Source/SwiftLintCore/Rules/CustomRules.swift b/Source/SwiftLintCore/Rules/CustomRules.swift index e523c757a7..ec6a4f9000 100644 --- a/Source/SwiftLintCore/Rules/CustomRules.swift +++ b/Source/SwiftLintCore/Rules/CustomRules.swift @@ -90,7 +90,7 @@ struct CustomRules: Rule, CacheDescriptionProvider { func canBeDisabled(violation: StyleViolation, by ruleID: RuleIdentifier) -> Bool { switch ruleID { case let .single(identifier: id): - id == Self.description.identifier + id == Self.identifier ? customRuleIdentifiers.contains(violation.ruleIdentifier) : customRuleIdentifiers.contains(id) && violation.ruleIdentifier == id default: @@ -101,10 +101,10 @@ struct CustomRules: Rule, CacheDescriptionProvider { func isEnabled(in region: Region, for ruleID: String) -> Bool { if !Self.description.allIdentifiers.contains(ruleID), !customRuleIdentifiers.contains(ruleID), - Self.description.identifier != ruleID { + Self.identifier != ruleID { return true } - return !region.disabledRuleIdentifiers.contains(RuleIdentifier(Self.description.identifier)) + return !region.disabledRuleIdentifiers.contains(RuleIdentifier(Self.identifier)) && !region.disabledRuleIdentifiers.contains(RuleIdentifier(ruleID)) && !region.disabledRuleIdentifiers.contains(.all) } diff --git a/Source/SwiftLintFramework/RulesFilter.swift b/Source/SwiftLintFramework/RulesFilter.swift index 9f585f7be4..8a5d1d250e 100644 --- a/Source/SwiftLintFramework/RulesFilter.swift +++ b/Source/SwiftLintFramework/RulesFilter.swift @@ -26,7 +26,7 @@ package final class RulesFilter { let filtered: [any Rule.Type] = allRules.list.compactMap { ruleID, ruleType in let enabledRule = enabledRules.first { rule in - type(of: rule).description.identifier == ruleID + type(of: rule).identifier == ruleID } let isRuleEnabled = enabledRule != nil diff --git a/Tests/CLITests/RulesFilterTests.swift b/Tests/CLITests/RulesFilterTests.swift index 7c2f3fcfa4..8979db66c1 100644 --- a/Tests/CLITests/RulesFilterTests.swift +++ b/Tests/CLITests/RulesFilterTests.swift @@ -23,7 +23,7 @@ final class RulesFilterTests: XCTestCase { XCTAssertEqual( Set(filteredRules.list.keys), - Set([RuleMock2.description.identifier]) + Set([RuleMock2.identifier]) ) } @@ -48,7 +48,7 @@ final class RulesFilterTests: XCTestCase { XCTAssertEqual( Set(filteredRules.list.keys), - Set([RuleMock1.description.identifier, CorrectableRuleMock.description.identifier]) + Set([RuleMock1.identifier, CorrectableRuleMock.identifier]) ) } @@ -73,7 +73,7 @@ final class RulesFilterTests: XCTestCase { XCTAssertEqual( Set(filteredRules.list.keys), - Set([CorrectableRuleMock.description.identifier]) + Set([CorrectableRuleMock.identifier]) ) } @@ -98,7 +98,7 @@ final class RulesFilterTests: XCTestCase { XCTAssertEqual( Set(filteredRules.list.keys), - Set([CorrectableRuleMock.description.identifier]) + Set([CorrectableRuleMock.identifier]) ) } @@ -122,7 +122,7 @@ final class RulesFilterTests: XCTestCase { XCTAssertEqual( Set(filteredRules.list.keys), - Set([CorrectableRuleMock.description.identifier]) + Set([CorrectableRuleMock.identifier]) ) } } diff --git a/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift b/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift index bf39d734f5..68460fd73b 100644 --- a/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift @@ -2,7 +2,7 @@ import XCTest final class CompilerProtocolInitRuleTests: SwiftLintTestCase { - private let ruleID = CompilerProtocolInitRule.description.identifier + private let ruleID = CompilerProtocolInitRule.identifier func testViolationMessageForExpressibleByIntegerLiteral() throws { let config = try XCTUnwrap(makeConfig(nil, ruleID)) diff --git a/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift b/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift index 9460f22367..1066d61010 100644 --- a/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift @@ -120,7 +120,7 @@ final class ComputedAccessorsOrderRuleTests: SwiftLintTestCase { } private func ruleViolations(_ example: Example, ruleConfiguration: Any? = nil) -> [StyleViolation] { - guard let config = makeConfig(ruleConfiguration, ComputedAccessorsOrderRule.description.identifier) else { + guard let config = makeConfig(ruleConfiguration, ComputedAccessorsOrderRule.identifier) else { return [] } diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift index 7f9367dcef..c20e8b90b5 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift @@ -28,7 +28,7 @@ final class ConfigurationAliasesTests: SwiftLintTestCase { // swiftlint:disable:next force_try let configuration = try! Configuration(dict: ["only_rules": ["mock"]], ruleList: testRuleList) let configuredIdentifiers = configuration.rules.map { - type(of: $0).description.identifier + type(of: $0).identifier } XCTAssertEqual(configuredIdentifiers, ["severity_level_mock"]) } diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift index 2c3c742e66..e928221e9b 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift @@ -55,8 +55,8 @@ extension ConfigurationTests { rulesMode: .defaultConfiguration( disabled: [], optIn: [ - ForceTryRule.description.identifier, - ForceCastRule.description.identifier, + ForceTryRule.identifier, + ForceCastRule.identifier, ] ) ) @@ -78,7 +78,7 @@ extension ConfigurationTests { } func testOnlyRuleMerging() { - let ruleIdentifier = TodoRule.description.identifier + let ruleIdentifier = TodoRule.identifier let onlyRuleConfiguration = Configuration.onlyRuleConfiguration(ruleIdentifier) let emptyDefaultConfiguration = Configuration.emptyDefaultConfiguration() @@ -90,7 +90,7 @@ extension ConfigurationTests { let mergedConfiguration2 = onlyRuleConfiguration.merged(withChild: disabledDefaultConfiguration) XCTAssertTrue(mergedConfiguration2.rules.isEmpty) - let enabledOnlyConfiguration = Configuration.enabledOnlyConfiguration(ForceTryRule.description.identifier) + let enabledOnlyConfiguration = Configuration.enabledOnlyConfiguration(ForceTryRule.identifier) let mergedConfiguration3 = onlyRuleConfiguration.merged(withChild: enabledOnlyConfiguration) XCTAssertEqual(mergedConfiguration3.rules.count, 1) XCTAssertTrue(mergedConfiguration3.rules[0] is TodoRule) @@ -362,7 +362,7 @@ extension ConfigurationTests { XCTAssertEqual(testCases.unique.count, 4 * 4) let ruleType = ImplicitReturnRule.self XCTAssertTrue((ruleType as Any) is any OptInRule.Type) - let ruleIdentifier = ruleType.description.identifier + let ruleIdentifier = ruleType.identifier for testCase in testCases { let parentConfiguration = Configuration(rulesMode: .defaultConfiguration( disabled: testCase.disabledInParent ? [ruleIdentifier] : [], @@ -396,7 +396,7 @@ extension ConfigurationTests { XCTAssertEqual(testCases.unique.count, 2 * 2) let ruleType = BlanketDisableCommandRule.self XCTAssertFalse(ruleType is any OptInRule.Type) - let ruleIdentifier = ruleType.description.identifier + let ruleIdentifier = ruleType.identifier for testCase in testCases { let parentConfiguration = Configuration( rulesMode: .defaultConfiguration(disabled: testCase.disabledInParent ? [ruleIdentifier] : [], optIn: []) @@ -428,7 +428,7 @@ extension ConfigurationTests { XCTAssertEqual(testCases.unique.count, 2 * 2) let ruleType = ImplicitReturnRule.self XCTAssertTrue((ruleType as Any) is any OptInRule.Type) - let ruleIdentifier = ruleType.description.identifier + let ruleIdentifier = ruleType.identifier let parentConfiguration = Configuration(rulesMode: .onlyConfiguration([ruleIdentifier])) for testCase in testCases { let childConfiguration = Configuration(rulesMode: .defaultConfiguration( @@ -635,8 +635,8 @@ extension ConfigurationTests { ) XCTAssertEqual( - configuration1.rules.map { type(of: $0).description.identifier }, - configuration2.rules.map { type(of: $0).description.identifier } + configuration1.rules.map { type(of: $0).identifier }, + configuration2.rules.map { type(of: $0).identifier } ) XCTAssertEqual( diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index f718aed784..ffca2d57ab 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -116,7 +116,7 @@ final class ConfigurationTests: SwiftLintTestCase { let config = try Configuration(dict: ["only_rules": only]) let configuredIdentifiers = config.rules.map { - type(of: $0).description.identifier + type(of: $0).identifier }.sorted() XCTAssertEqual(only, configuredIdentifiers) } @@ -178,7 +178,7 @@ final class ConfigurationTests: SwiftLintTestCase { let expectedIdentifiers = Set(RuleRegistry.shared.list.list.keys .filter({ !(["nesting", "todo"] + optInRules).contains($0) })) let configuredIdentifiers = Set(disabledConfig.rules.map { - type(of: $0).description.identifier + type(of: $0).identifier }) XCTAssertEqual(expectedIdentifiers, configuredIdentifiers) } @@ -205,7 +205,7 @@ final class ConfigurationTests: SwiftLintTestCase { let duplicateConfig2 = try? Configuration(dict: ["opt_in_rules": [optInRules.first!, optInRules.first!]]) XCTAssertEqual( - duplicateConfig2?.rules.filter { type(of: $0).description.identifier == optInRules.first! }.count, 1, + duplicateConfig2?.rules.filter { type(of: $0).identifier == optInRules.first! }.count, 1, "duplicate rules should be removed when initializing Configuration" ) @@ -435,14 +435,14 @@ final class ConfigurationTests: SwiftLintTestCase { func testConfiguresCorrectlyFromDict() throws { let ruleConfiguration = [1, 2] - let config = [RuleWithLevelsMock.description.identifier: ruleConfiguration] + let config = [RuleWithLevelsMock.identifier: ruleConfiguration] let rules = try testRuleList.allRulesWrapped(configurationDict: config).map(\.rule) // swiftlint:disable:next xct_specific_matcher XCTAssertTrue(rules == [try RuleWithLevelsMock(configuration: ruleConfiguration)]) } func testConfigureFallsBackCorrectly() throws { - let config = [RuleWithLevelsMock.description.identifier: ["a", "b"]] + let config = [RuleWithLevelsMock.identifier: ["a", "b"]] let rules = try testRuleList.allRulesWrapped(configurationDict: config).map(\.rule) // swiftlint:disable:next xct_specific_matcher XCTAssertTrue(rules == [RuleWithLevelsMock()]) diff --git a/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift b/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift index f8691673cd..83c162fba3 100644 --- a/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift @@ -21,7 +21,7 @@ final class ContainsOverFirstNotNilRuleTests: SwiftLintTestCase { // MARK: - Private private func violations(_ example: Example, config: Any? = nil) -> [StyleViolation] { - guard let config = makeConfig(config, ContainsOverFirstNotNilRule.description.identifier) else { + guard let config = makeConfig(config, ContainsOverFirstNotNilRule.identifier) else { return [] } diff --git a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift index 548fb98db2..4a4f11006c 100644 --- a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift @@ -66,7 +66,7 @@ final class CustomRulesTests: SwiftLintTestCase { func testCustomRuleConfigurationThrows() { let config = 17 var customRulesConfig = CustomRulesConfiguration() - checkError(Issue.invalidConfiguration(ruleID: CustomRules.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: CustomRules.identifier)) { try customRulesConfig.apply(configuration: config) } } @@ -106,7 +106,7 @@ final class CustomRulesTests: SwiftLintTestCase { XCTAssertEqual(customRulesConfig.customRuleConfigurations.count, 1) - let identifier = customRulesConfig.customRuleConfigurations.first?.description.identifier + let identifier = customRulesConfig.customRuleConfigurations.first?.identifier XCTAssertEqual(identifier, "my_custom_rule") } @@ -570,7 +570,7 @@ final class CustomRulesTests: SwiftLintTestCase { private extension StyleViolation { func isSuperfluousDisableCommandViolation(for ruleIdentifier: String) -> Bool { - self.ruleIdentifier == SuperfluousDisableCommandRule.description.identifier && + self.ruleIdentifier == SuperfluousDisableCommandRule.identifier && reason.contains("SwiftLint rule '\(ruleIdentifier)' did not trigger a violation") } } diff --git a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift index a03337b708..eb9638a82f 100644 --- a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift @@ -71,7 +71,7 @@ final class CyclomaticComplexityConfigurationTests: SwiftLintTestCase { var configuration = CyclomaticComplexityConfiguration( length: SeverityLevelsConfiguration(warning: 100, error: 150) ) - checkError(Issue.invalidConfiguration(ruleID: CyclomaticComplexityRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: CyclomaticComplexityRule.identifier)) { try configuration.apply(configuration: badConfig) } } diff --git a/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift index b130557f09..f9edd35491 100644 --- a/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift @@ -139,7 +139,7 @@ final class DeploymentTargetConfigurationTests: SwiftLintTestCase { for badConfig in badConfigs { var configuration = DeploymentTargetConfiguration() - checkError(Issue.invalidConfiguration(ruleID: DeploymentTargetRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: DeploymentTargetRule.identifier)) { try configuration.apply(configuration: badConfig) } } diff --git a/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift b/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift index 46c8b21066..b101b89b0c 100644 --- a/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift @@ -35,7 +35,7 @@ final class DeploymentTargetRuleTests: SwiftLintTestCase { } private func violations(_ example: Example, config: Any?) -> [StyleViolation] { - guard let config = makeConfig(config, DeploymentTargetRule.description.identifier) else { + guard let config = makeConfig(config, DeploymentTargetRule.identifier) else { return [] } diff --git a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift b/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift index 06f890bdea..a95bb4ec5a 100644 --- a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift @@ -191,7 +191,7 @@ final class ExpiringTodoRuleTests: SwiftLintTestCase { ] } - return makeConfig(serializedConfig, ExpiringTodoRule.description.identifier)! + return makeConfig(serializedConfig, ExpiringTodoRule.identifier)! } } diff --git a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift index ea362d5228..ec413e59a4 100644 --- a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift @@ -33,14 +33,14 @@ final class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase { func testInvalidTypeOfCustomConfiguration() { var config = ExplicitTypeInterfaceConfiguration() - checkError(Issue.invalidConfiguration(ruleID: ExplicitTypeInterfaceRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ExplicitTypeInterfaceRule.identifier)) { try config.apply(configuration: "invalidKey") } } func testInvalidTypeOfValueInCustomConfiguration() { var config = ExplicitTypeInterfaceConfiguration() - checkError(Issue.invalidConfiguration(ruleID: ExplicitTypeInterfaceRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ExplicitTypeInterfaceRule.identifier)) { try config.apply(configuration: ["severity": "foo"]) } } diff --git a/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift b/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift index 04e9a6cbab..d849b2996d 100644 --- a/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift @@ -101,7 +101,7 @@ final class FunctionBodyLengthRuleTests: SwiftLintTestCase { } private func violations(_ example: Example, configuration: Any? = nil) -> [StyleViolation] { - let config = makeConfig(configuration, FunctionBodyLengthRule.description.identifier)! + let config = makeConfig(configuration, FunctionBodyLengthRule.identifier)! return SwiftLintFrameworkTests.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift b/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift index 8a83b92bbe..0905bf01f9 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift @@ -3,7 +3,7 @@ import XCTest final class ImplicitGetterRuleTests: SwiftLintTestCase { func testPropertyReason() throws { - let config = try XCTUnwrap(makeConfig(nil, ImplicitGetterRule.description.identifier)) + let config = try XCTUnwrap(makeConfig(nil, ImplicitGetterRule.identifier)) let example = Example(""" class Foo { var foo: Int { @@ -20,7 +20,7 @@ final class ImplicitGetterRuleTests: SwiftLintTestCase { } func testSubscriptReason() throws { - let config = try XCTUnwrap(makeConfig(nil, ImplicitGetterRule.description.identifier)) + let config = try XCTUnwrap(makeConfig(nil, ImplicitGetterRule.identifier)) let example = Example(""" class Foo { subscript(i: Int) -> Int { diff --git a/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift index c6a62deca1..b19dd28e02 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift @@ -31,7 +31,7 @@ final class ImplicitReturnConfigurationTests: SwiftLintTestCase { var configuration = ImplicitReturnConfiguration() let config = ["included": ["foreach"]] as [String: any Sendable] - checkError(Issue.invalidConfiguration(ruleID: ImplicitReturnRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ImplicitReturnRule.identifier)) { try configuration.apply(configuration: config) } } diff --git a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift index 6dca068e61..6ba01c625c 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift @@ -39,7 +39,7 @@ final class ImplicitlyUnwrappedOptionalConfigurationTests: SwiftLintTestCase { mode: .allExceptIBOutlets ) - checkError(Issue.invalidConfiguration(ruleID: ImplicitlyUnwrappedOptionalRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ImplicitlyUnwrappedOptionalRule.identifier)) { try configuration.apply(configuration: badConfig) } } diff --git a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift b/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift index 627797f814..b0234978bc 100644 --- a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift @@ -282,7 +282,7 @@ final class IndentationWidthRuleTests: SwiftLintTestCase { configDict["include_compiler_directives"] = includeCompilerDirectives configDict["include_multiline_strings"] = includeMultilineStrings - guard let config = makeConfig(configDict, IndentationWidthRule.description.identifier) else { + guard let config = makeConfig(configDict, IndentationWidthRule.identifier) else { XCTFail("Unable to create rule configuration.", file: (file), line: line) return 0 } diff --git a/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift index 01b59c8c9d..55360aabe8 100644 --- a/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift @@ -71,7 +71,7 @@ final class LineLengthConfigurationTests: SwiftLintTestCase { func testLineLengthConfigurationThrowsOnBadConfig() { let config = ["warning": "unknown"] var configuration = LineLengthConfiguration(length: severityLevels) - checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.identifier)) { try configuration.apply(configuration: config) } } @@ -84,7 +84,7 @@ final class LineLengthConfigurationTests: SwiftLintTestCase { for badConfig in badConfigs { var configuration = LineLengthConfiguration(length: severityLevels) - checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.identifier)) { try configuration.apply(configuration: badConfig) } } diff --git a/Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift b/Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift index 4df00865d1..fdcff1f16f 100644 --- a/Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift +++ b/Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift @@ -383,7 +383,7 @@ final class ModifierOrderTests: SwiftLintTestCase { } func testViolationMessage() { - let ruleID = ModifierOrderRule.description.identifier + let ruleID = ModifierOrderRule.identifier guard let config = makeConfig(["preferred_modifier_order": ["acl", "final"]], ruleID) else { XCTFail("Failed to create configuration") return diff --git a/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift index 9f7fac7979..fc39b2bf29 100644 --- a/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift @@ -67,7 +67,7 @@ final class NameConfigurationTests: SwiftLintTestCase { minLengthError: 0, maxLengthWarning: 0, maxLengthError: 0) - checkError(Issue.invalidConfiguration(ruleID: RuleMock.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: RuleMock.identifier)) { try nameConfig.apply(configuration: config) } } diff --git a/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift b/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift index 4386d10d4f..435ca660be 100644 --- a/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift @@ -200,7 +200,7 @@ final class NestingRuleTests: SwiftLintTestCase { }) let description = RuleDescription( - identifier: NestingRule.description.identifier, + identifier: NestingRule.identifier, name: NestingRule.description.name, description: NestingRule.description.description, kind: .metrics, @@ -499,7 +499,7 @@ final class NestingRuleTests: SwiftLintTestCase { ]) let description = RuleDescription( - identifier: NestingRule.description.identifier, + identifier: NestingRule.identifier, name: NestingRule.description.name, description: NestingRule.description.description, kind: .metrics, diff --git a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift index 851f358587..22b22f58d6 100644 --- a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift @@ -31,14 +31,14 @@ final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { func testInvalidTypeOfCustomConfiguration() { var config = NoEmptyBlockConfiguration() - checkError(Issue.invalidConfiguration(ruleID: NoEmptyBlockRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: NoEmptyBlockRule.identifier)) { try config.apply(configuration: "invalidKey") } } func testInvalidTypeOfValueInCustomConfiguration() { var config = NoEmptyBlockConfiguration() - checkError(Issue.invalidConfiguration(ruleID: NoEmptyBlockRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: NoEmptyBlockRule.identifier)) { try config.apply(configuration: ["severity": "foo"]) } } diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift index db0a56ddb9..f86fca1e21 100644 --- a/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift @@ -37,7 +37,7 @@ final class RuleConfigurationTests: SwiftLintTestCase { func testNestingConfigurationThrowsOnBadConfig() { let config = 17 var nestingConfig = defaultNestingConfiguration - checkError(Issue.invalidConfiguration(ruleID: NestingRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: NestingRule.identifier)) { try nestingConfig.apply(configuration: config) } } @@ -117,7 +117,7 @@ final class RuleConfigurationTests: SwiftLintTestCase { func testRegexConfigurationThrows() { let config = 17 var regexConfig = RegexConfiguration(identifier: "") - checkError(Issue.invalidConfiguration(ruleID: RuleMock.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: RuleMock.identifier)) { try regexConfig.apply(configuration: config) } } @@ -137,7 +137,7 @@ final class RuleConfigurationTests: SwiftLintTestCase { let config = "unknown" var configuration = TrailingWhitespaceConfiguration(ignoresEmptyLines: false, ignoresComments: true) - checkError(Issue.invalidConfiguration(ruleID: TrailingWhitespaceRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: TrailingWhitespaceRule.identifier)) { try configuration.apply(configuration: config) } } @@ -321,7 +321,7 @@ final class RuleConfigurationTests: SwiftLintTestCase { var configuration = ModifierOrderConfiguration() let config = ["severity": "warning", "preferred_modifier_order": ["specialize"]] as [String: any Sendable] - checkError(Issue.invalidConfiguration(ruleID: ModifierOrderRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ModifierOrderRule.identifier)) { try configuration.apply(configuration: config) } } @@ -329,7 +329,7 @@ final class RuleConfigurationTests: SwiftLintTestCase { func testModifierOrderConfigurationThrowsOnNonModifiableGroup() { var configuration = ModifierOrderConfiguration() let config = ["severity": "warning", "preferred_modifier_order": ["atPrefixed"]] as [String: any Sendable] - checkError(Issue.invalidConfiguration(ruleID: ModifierOrderRule.description.identifier)) { + checkError(Issue.invalidConfiguration(ruleID: ModifierOrderRule.identifier)) { try configuration.apply(configuration: config) } } diff --git a/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift b/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift index 8058742dae..8130bc54cc 100644 --- a/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift @@ -41,7 +41,7 @@ final class TodoRuleTests: SwiftLintTestCase { } private func violations(_ example: Example, config: Any? = nil) -> [StyleViolation] { - let config = makeConfig(config, TodoRule.description.identifier)! + let config = makeConfig(config, TodoRule.identifier)! return SwiftLintFrameworkTests.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift b/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift index dc26b39ebb..e7b4a8f361 100644 --- a/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift @@ -69,7 +69,7 @@ final class TrailingCommaRuleTests: SwiftLintTestCase { } private func trailingCommaViolations(_ example: Example, ruleConfiguration: Any? = nil) -> [StyleViolation] { - let config = makeConfig(ruleConfiguration, TrailingCommaRule.description.identifier)! + let config = makeConfig(ruleConfiguration, TrailingCommaRule.identifier)! return violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift b/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift index 20d725b487..deba0b0844 100644 --- a/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift @@ -2,7 +2,7 @@ import XCTest final class VerticalWhitespaceRuleTests: SwiftLintTestCase { - private let ruleID = VerticalWhitespaceRule.description.identifier + private let ruleID = VerticalWhitespaceRule.identifier func testAttributesWithMaxEmptyLines() { // Test with custom `max_empty_lines` diff --git a/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift b/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift index d32351bbe2..90c78cfada 100644 --- a/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift @@ -181,7 +181,7 @@ final class XCTSpecificMatcherRuleTests: SwiftLintTestCase { } private func violations(_ example: Example) -> [StyleViolation] { - guard let config = makeConfig(nil, XCTSpecificMatcherRule.description.identifier) else { return [] } + guard let config = makeConfig(nil, XCTSpecificMatcherRule.identifier) else { return [] } return SwiftLintFrameworkTests.violations(example, config: config) } diff --git a/Tests/SwiftLintTestHelpers/TestHelpers.swift b/Tests/SwiftLintTestHelpers/TestHelpers.swift index c28f43e055..06b09ac8b5 100644 --- a/Tests/SwiftLintTestHelpers/TestHelpers.swift +++ b/Tests/SwiftLintTestHelpers/TestHelpers.swift @@ -272,7 +272,7 @@ private extension String { public func makeConfig(_ ruleConfiguration: Any?, _ identifier: String, skipDisableCommandTests: Bool = false) -> Configuration? { - let superfluousDisableCommandRuleIdentifier = SuperfluousDisableCommandRule.description.identifier + let superfluousDisableCommandRuleIdentifier = SuperfluousDisableCommandRule.identifier let identifiers: Set = skipDisableCommandTests ? [identifier] : [identifier, superfluousDisableCommandRuleIdentifier] @@ -300,7 +300,7 @@ private func testCorrection(_ correction: (Example, Example), var config = configuration if let correctionConfiguration = correction.0.configuration, case let .onlyConfiguration(onlyRules) = configuration.rulesMode, - let ruleToConfigure = (onlyRules.first { $0 != SuperfluousDisableCommandRule.description.identifier }), + let ruleToConfigure = (onlyRules.first { $0 != SuperfluousDisableCommandRule.identifier }), case let configDict: [_: any Sendable] = ["only_rules": onlyRules, ruleToConfigure: correctionConfiguration], let typedConfiguration = try? Configuration(dict: configDict) { config = configuration.merged(withChild: typedConfiguration, rootDirectory: configuration.rootDirectory) @@ -425,7 +425,7 @@ public extension XCTestCase { for trigger in disabledTriggers { let violationsPartitionedByType = makeViolations(trigger) - .partitioned { $0.ruleIdentifier == SuperfluousDisableCommandRule.description.identifier } + .partitioned { $0.ruleIdentifier == SuperfluousDisableCommandRule.identifier } XCTAssert(violationsPartitionedByType.first.isEmpty, "Violation(s) still triggered although rule was disabled", From f410ea51a7ac242070f3e4e2a88dc2519ea9aabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 30 Oct 2024 14:24:22 +0100 Subject: [PATCH 073/260] Support Swift version 6.0.2 (#5844) --- Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift index fb103a7b7e..aff4993cc8 100644 --- a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift +++ b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift @@ -3,7 +3,9 @@ import XCTest final class SwiftVersionTests: SwiftLintTestCase { func testDetectSwiftVersion() { -#if compiler(>=6.0.1) +#if compiler(>=6.0.2) + let version = "6.0.2" +#elseif compiler(>=6.0.1) let version = "6.0.1" #elseif compiler(>=6.0.0) let version = "6.0.0" From 23d6a7c3f56f2d0815774415a12b5405dfd1d9b0 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Wed, 30 Oct 2024 22:03:54 +0000 Subject: [PATCH 074/260] Added `lenient` command line option (#5801) --- CHANGELOG.md | 5 ++ README.md | 3 + .../Extensions/Configuration+Merging.swift | 1 + .../Extensions/Configuration+Parsing.swift | 2 + .../SwiftLintCore/Models/Configuration.swift | 11 +++ .../LintOrAnalyzeCommand.swift | 26 +++++-- .../ConfigurationTests.swift | 6 ++ .../LintOrAnalyzeOptionsTests.swift | 72 +++++++++++++++++++ 8 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f2100741..ffb00ebc19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ argument `--use-script-input-file-lists`. [BlueVirusX](https://github.com/BlueVirusX) +* Adds a `lenient` configuration file setting, equivalent to the `--lenient` + command line option. + [Martin Redington](https://github.com/mildm8nnered) + [#5801](https://github.com/realm/SwiftLint/issues/5801) + #### Bug Fixes * Run command plugin in whole package if no targets are defined in the diff --git a/README.md b/README.md index 2d37e03326..39b7294f7e 100644 --- a/README.md +++ b/README.md @@ -723,6 +723,9 @@ allow_zero_lintable_files: false # If true, SwiftLint will treat all warnings as errors. strict: false +# If true, SwiftLint will treat all errors as warnings. +lenient: false + # The path to a baseline file, which will be used to filter out detected violations. baseline: Baseline.json diff --git a/Source/SwiftLintCore/Extensions/Configuration+Merging.swift b/Source/SwiftLintCore/Extensions/Configuration+Merging.swift index 05e5dfd490..82a11e342d 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Merging.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Merging.swift @@ -26,6 +26,7 @@ extension Configuration { cachePath: cachePath, allowZeroLintableFiles: childConfiguration.allowZeroLintableFiles, strict: childConfiguration.strict, + lenient: childConfiguration.lenient, baseline: childConfiguration.baseline, writeBaseline: childConfiguration.writeBaseline, checkForUpdates: childConfiguration.checkForUpdates diff --git a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift index 54dcf399da..c8326c0776 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift +++ b/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift @@ -15,6 +15,7 @@ extension Configuration { case analyzerRules = "analyzer_rules" case allowZeroLintableFiles = "allow_zero_lintable_files" case strict = "strict" + case lenient = "lenient" case baseline = "baseline" case writeBaseline = "write_baseline" case checkForUpdates = "check_for_updates" @@ -103,6 +104,7 @@ extension Configuration { pinnedVersion: dict[Key.swiftlintVersion.rawValue].map { ($0 as? String) ?? String(describing: $0) }, allowZeroLintableFiles: dict[Key.allowZeroLintableFiles.rawValue] as? Bool ?? false, strict: dict[Key.strict.rawValue] as? Bool ?? false, + lenient: dict[Key.lenient.rawValue] as? Bool ?? false, baseline: dict[Key.baseline.rawValue] as? String, writeBaseline: dict[Key.writeBaseline.rawValue] as? String, checkForUpdates: dict[Key.checkForUpdates.rawValue] as? Bool ?? false diff --git a/Source/SwiftLintCore/Models/Configuration.swift b/Source/SwiftLintCore/Models/Configuration.swift index b8169afe11..bf5d6d9bc1 100644 --- a/Source/SwiftLintCore/Models/Configuration.swift +++ b/Source/SwiftLintCore/Models/Configuration.swift @@ -38,6 +38,9 @@ public struct Configuration { /// Treat warnings as errors. public let strict: Bool + /// Treat errors as warnings. + public let lenient: Bool + /// The path to read a baseline from. public let baseline: String? @@ -83,6 +86,7 @@ public struct Configuration { cachePath: String?, allowZeroLintableFiles: Bool, strict: Bool, + lenient: Bool, baseline: String?, writeBaseline: String?, checkForUpdates: Bool @@ -97,6 +101,7 @@ public struct Configuration { self.cachePath = cachePath self.allowZeroLintableFiles = allowZeroLintableFiles self.strict = strict + self.lenient = lenient self.baseline = baseline self.writeBaseline = writeBaseline self.checkForUpdates = checkForUpdates @@ -117,6 +122,7 @@ public struct Configuration { cachePath = configuration.cachePath allowZeroLintableFiles = configuration.allowZeroLintableFiles strict = configuration.strict + lenient = configuration.lenient baseline = configuration.baseline writeBaseline = configuration.writeBaseline checkForUpdates = configuration.checkForUpdates @@ -143,6 +149,7 @@ public struct Configuration { /// - parameter allowZeroLintableFiles: Allow SwiftLint to exit successfully when passed ignored or unlintable /// files. /// - parameter strict: Treat warnings as errors. + /// - parameter lenient: Treat errors as warnings. /// - parameter baseline: The path to read a baseline from. /// - parameter writeBaseline: The path to write a baseline to. /// - parameter checkForUpdates: Check for updates to SwiftLint. @@ -160,6 +167,7 @@ public struct Configuration { pinnedVersion: String? = nil, allowZeroLintableFiles: Bool = false, strict: Bool = false, + lenient: Bool = false, baseline: String? = nil, writeBaseline: String? = nil, checkForUpdates: Bool = false @@ -189,6 +197,7 @@ public struct Configuration { cachePath: cachePath, allowZeroLintableFiles: allowZeroLintableFiles, strict: strict, + lenient: lenient, baseline: baseline, writeBaseline: writeBaseline, checkForUpdates: checkForUpdates @@ -304,6 +313,7 @@ extension Configuration: Hashable { hasher.combine(reporter) hasher.combine(allowZeroLintableFiles) hasher.combine(strict) + hasher.combine(lenient) hasher.combine(baseline) hasher.combine(writeBaseline) hasher.combine(checkForUpdates) @@ -325,6 +335,7 @@ extension Configuration: Hashable { lhs.fileGraph == rhs.fileGraph && lhs.allowZeroLintableFiles == rhs.allowZeroLintableFiles && lhs.strict == rhs.strict && + lhs.lenient == rhs.lenient && lhs.baseline == rhs.baseline && lhs.writeBaseline == rhs.writeBaseline && lhs.checkForUpdates == rhs.checkForUpdates && diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index f9e24151b4..746342c301 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -169,6 +169,7 @@ package struct LintOrAnalyzeCommand { currentViolations = applyLeniency( options: options, strict: builder.configuration.strict, + lenient: builder.configuration.lenient, violations: violationsBeforeLeniency ) visitorMutationQueue.sync { @@ -179,6 +180,7 @@ package struct LintOrAnalyzeCommand { currentViolations = applyLeniency( options: options, strict: builder.configuration.strict, + lenient: builder.configuration.lenient, violations: linter.styleViolations(using: builder.storage) ) } @@ -273,15 +275,16 @@ package struct LintOrAnalyzeCommand { private static func applyLeniency( options: LintOrAnalyzeOptions, strict: Bool, + lenient: Bool, violations: [StyleViolation] ) -> [StyleViolation] { - let strict = (strict && !options.lenient) || options.strict + let leniency = options.leniency(strict: strict, lenient: lenient) - switch (options.lenient, strict) { + switch leniency { case (false, false): return violations - case (true, false): + case (false, true): return violations.map { if $0.severity == .error { return $0.with(severity: .warning) @@ -289,7 +292,7 @@ package struct LintOrAnalyzeCommand { return $0 } - case (false, true): + case (true, false): return violations.map { if $0.severity == .warning { return $0.with(severity: .error) @@ -298,7 +301,7 @@ package struct LintOrAnalyzeCommand { } case (true, true): - queuedFatalError("Invalid command line options: 'lenient' and 'strict' are mutually exclusive.") + queuedFatalError("Invalid command line or config options: 'strict' and 'lenient' are mutually exclusive.") } } @@ -394,8 +397,8 @@ private class LintOrAnalyzeResultBuilder { } } -private extension LintOrAnalyzeOptions { - func writeToOutput(_ string: String) { +extension LintOrAnalyzeOptions { + fileprivate func writeToOutput(_ string: String) { guard let outFile = output else { queuedPrint(string) return @@ -411,6 +414,15 @@ private extension LintOrAnalyzeOptions { Issue.fileNotWritable(path: outFile).print() } } + + typealias Leniency = (strict: Bool, lenient: Bool) + + // Config file settings can be overridden by either `--strict` or `--lenient` command line options. + func leniency(strict configurationStrict: Bool, lenient configurationLenient: Bool) -> Leniency { + let strict = self.strict || (configurationStrict && !self.lenient) + let lenient = self.lenient || (configurationLenient && !self.strict) + return Leniency(strict: strict, lenient: lenient) + } } private actor CorrectionsBuilder { diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index ffca2d57ab..9fd493a068 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -56,6 +56,7 @@ final class ConfigurationTests: SwiftLintTestCase { XCTAssertEqual(reporterFrom(identifier: config.reporter).identifier, "xcode") XCTAssertFalse(config.allowZeroLintableFiles) XCTAssertFalse(config.strict) + XCTAssertFalse(config.lenient) XCTAssertNil(config.baseline) XCTAssertNil(config.writeBaseline) XCTAssertFalse(config.checkForUpdates) @@ -458,6 +459,11 @@ final class ConfigurationTests: SwiftLintTestCase { XCTAssertTrue(configuration.strict) } + func testLenient() throws { + let configuration = try Configuration(dict: ["lenient": true]) + XCTAssertTrue(configuration.lenient) + } + func testBaseline() throws { let baselinePath = "Baseline.json" let configuration = try Configuration(dict: ["baseline": baselinePath]) diff --git a/Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift b/Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift new file mode 100644 index 0000000000..07ec490791 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift @@ -0,0 +1,72 @@ +@testable import SwiftLintFramework +import XCTest + +final class LintOrAnalyzeOptionsTests: XCTestCase { + private typealias Leniency = LintOrAnalyzeOptions.Leniency + + func testLeniency() { + let parameters = [ + Leniency(strict: false, lenient: false), + Leniency(strict: true, lenient: true), + Leniency(strict: true, lenient: false), + Leniency(strict: false, lenient: true), + ] + + for commandLine in parameters { + let options = LintOrAnalyzeOptions(leniency: commandLine) + for configuration in parameters { + let leniency = options.leniency(strict: configuration.strict, lenient: configuration.lenient) + if commandLine.strict { + // Command line takes precedence. + XCTAssertTrue(leniency.strict) + if !commandLine.lenient { + // `--strict` should disable configuration lenience. + XCTAssertFalse(leniency.lenient) + } + } else if commandLine.lenient { + // Command line takes precedence, and should override + // `strict` in the configuration. + XCTAssertTrue(leniency.lenient) + XCTAssertFalse(leniency.strict) + } else if configuration.strict { + XCTAssertTrue(leniency.strict) + } else if configuration.lenient { + XCTAssertTrue(leniency.lenient) + } + } + } + } +} + +private extension LintOrAnalyzeOptions { + init(leniency: Leniency) { + self.init(mode: .lint, + paths: [], + useSTDIN: true, + configurationFiles: [], + strict: leniency.strict, + lenient: leniency.lenient, + forceExclude: false, + useExcludingByPrefix: false, + useScriptInputFiles: false, + useScriptInputFileLists: false, + benchmark: false, + reporter: nil, + baseline: nil, + writeBaseline: nil, + workingDirectory: nil, + quiet: false, + output: nil, + progress: false, + cachePath: nil, + ignoreCache: false, + enableAllRules: false, + onlyRule: [], + autocorrect: false, + format: false, + compilerLogPath: nil, + compileCommands: nil, + checkForUpdates: false + ) + } +} From a7318316d997b8db8ee440fab1b9312a2a4b9287 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 11:14:54 -0500 Subject: [PATCH 075/260] Bump rexml from 3.3.6 to 3.3.9 (#5842) Bumps the bundler group with 1 update in the / directory: [rexml](https://github.com/ruby/rexml). Updates `rexml` from 3.3.6 to 3.3.9 - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.6...v3.3.9) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect dependency-group: bundler ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5e6967364c..c0a88b3b3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,8 +122,7 @@ GEM public_suffix (4.0.7) rchardet (1.8.0) redcarpet (3.6.0) - rexml (3.3.6) - strscan + rexml (3.3.9) rouge (4.3.0) ruby-macho (2.5.1) ruby2_keywords (0.0.5) @@ -134,7 +133,6 @@ GEM faraday (>= 0.17.3, < 3) sqlite3 (1.7.3-arm64-darwin) sqlite3 (1.7.3-x86_64-linux) - strscan (3.1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) typhoeus (1.4.0) From 3344fc6d31b921b333f601b185f2da75e342a567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 10 Nov 2024 15:32:16 +0100 Subject: [PATCH 076/260] Add cache path and directories only when commands accept them (#5851) --- CHANGELOG.md | 5 +++++ .../SwiftLintCommandPlugin.swift | 22 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb00ebc19..cfdf749b3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5167](https://github.com/realm/SwiftLint/issues/5167) +* Only pass cache path and directory paths to commands that accept these arguments + in the command plugin. + [SimplyDanny](https://github.com/SimplyDanny) + [#5848](https://github.com/realm/SwiftLint/issues/5848) + * Do not throw deprecation warning if deprecated property is not presented in configuration. [chipp](https://github.com/chipp) diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index d254ff8fdd..e1f562fd18 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -1,6 +1,19 @@ import Foundation import PackagePlugin +private let commandsNotExpectingPaths: Set = [ + "docs", + "generate-docs", + "baseline", + "reporters", + "rules", + "version", +] + +private let commandsWithoutCachPathOption: Set = commandsNotExpectingPaths.union([ + "analyze", +]) + @main struct SwiftLintCommandPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) throws { @@ -13,7 +26,7 @@ struct SwiftLintCommandPlugin: CommandPlugin { let targets = targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames) - guard !targets.isEmpty else { + if targets.isEmpty || !commandsNotExpectingPaths.isDisjoint(with: arguments) { try run(with: context, arguments: arguments) return } @@ -34,11 +47,12 @@ struct SwiftLintCommandPlugin: CommandPlugin { process.currentDirectoryURL = URL(fileURLWithPath: context.package.directory.string) process.executableURL = URL(fileURLWithPath: try context.tool(named: "swiftlint").path.string) process.arguments = arguments - if !arguments.contains("analyze") { - // The analyze command does not support the `--cache-path` argument. + if commandsWithoutCachPathOption.isDisjoint(with: arguments) { process.arguments! += ["--cache-path", "\(context.pluginWorkDirectory.string)"] } - process.arguments! += [directory] + if commandsNotExpectingPaths.isDisjoint(with: arguments) { + process.arguments! += [directory] + } try process.run() process.waitUntilExit() From fb3ce5adb226878de61f4754591868a71d5ffa1e Mon Sep 17 00:00:00 2001 From: Jared Grubb <1256381+jaredgrubb@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:27:59 -0800 Subject: [PATCH 077/260] Add `ignore_properties` option to `redundant_type_annotation` rule (#5839) --- CHANGELOG.md | 7 ++++++ .../RedundantTypeAnnotationRule.swift | 25 ++++++++++++++++++- ...RedundantTypeAnnotationConfiguration.swift | 2 ++ .../default_rule_configurations.yml | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfdf749b3a..3be513f1c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,13 @@ [Martin Redington](https://github.com/mildm8nnered) [#5801](https://github.com/realm/SwiftLint/issues/5801) +* The `redundant_type_annotation` rule gains a new option, + `ignore_properties`, that skips enforcement on members in a + type declaration (like a `struct`). This helps the rule coexist with + the `explicit_type_interface` rule that requires such redundancy. + [jaredgrubb](https://github.com/jaredgrubb) + [#3750](https://github.com/realm/SwiftLint/issues/3750) + #### Bug Fixes * Run command plugin in whole package if no targets are defined in the diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift index 019ff40ac0..ac7b3047bc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift @@ -57,6 +57,12 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule { Example("var dbl: Double = 0.0"), Example("var int: Int = 0"), Example("var str: String = \"str\""), + Example(""" + struct Foo { + var url: URL = URL() + let myVar: Int? = 0, s: String = "" + } + """, configuration: ["ignore_properties": true]), ], triggeringExamples: [ Example("var url↓:URL=URL()"), @@ -88,6 +94,13 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule { } } """), + Example(""" + class ViewController: UIViewController { + func someMethod() { + let myVar↓: Int = Int(5) + } + } + """, configuration: ["ignore_properties": true]), Example("let a↓: [Int] = [Int]()"), Example("let a↓: A.B = A.B()"), Example(""" @@ -183,7 +196,7 @@ private extension RedundantTypeAnnotationRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: PatternBindingSyntax) { if let varDecl = node.parent?.parent?.as(VariableDeclSyntax.self), - configuration.ignoreAttributes.allSatisfy({ !varDecl.attributes.contains(attributeNamed: $0) }), + !configuration.shouldSkipRuleCheck(for: varDecl), let typeAnnotation = node.typeAnnotation, let initializer = node.initializer?.value { collectViolation(forType: typeAnnotation, withInitializer: initializer) @@ -261,3 +274,13 @@ private extension SyntaxKind { } } } + +extension RedundantTypeAnnotationConfiguration { + func shouldSkipRuleCheck(for varDecl: VariableDeclSyntax) -> Bool { + if ignoreAttributes.contains(where: { varDecl.attributes.contains(attributeNamed: $0) }) { + return true + } + + return ignoreProperties && varDecl.parent?.is(MemberBlockItemSyntax.self) == true + } +} diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantTypeAnnotationConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantTypeAnnotationConfiguration.swift index 3eb9596298..7bb54dacfa 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantTypeAnnotationConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantTypeAnnotationConfiguration.swift @@ -8,6 +8,8 @@ struct RedundantTypeAnnotationConfiguration: SeverityBasedRuleConfiguration { var severityConfiguration = SeverityConfiguration(.warning) @ConfigurationElement(key: "ignore_attributes") var ignoreAttributes = Set(["IBInspectable"]) + @ConfigurationElement(key: "ignore_properties") + private(set) var ignoreProperties = false @ConfigurationElement(key: "consider_default_literal_types_redundant") private(set) var considerDefaultLiteralTypesRedundant = false } diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 57148068b8..f9014e5743 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -473,6 +473,7 @@ redundant_string_enum_value: redundant_type_annotation: severity: warning ignore_attributes: ["IBInspectable"] + ignore_properties: false consider_default_literal_types_redundant: false redundant_void_return: severity: warning From 707a63daf0ec5054e22fe32edc3614fa27efa5f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 16 Nov 2024 11:25:50 +0100 Subject: [PATCH 078/260] Allow closures being directly called to be wrapped into parentheses (#5856) --- CHANGELOG.md | 5 +++++ .../Rules/Style/ControlStatementRule.swift | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be513f1c8..f02dc71941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5787](https://github.com/realm/SwiftLint/issues/5787) +* Stop triggering the `control_statement` rule on closures being directly + called as conditions. + [SimplyDanny](https://github.com/SimplyDanny) + [#5846](https://github.com/realm/SwiftLint/issues/5846) + * Do not trigger `self_in_property_initialization` rule on `self` in key paths expressions. [SimplyDanny](https://github.com/SimplyDanny) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift index 8afd46d354..1ba85155d5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift @@ -29,6 +29,8 @@ struct ControlStatementRule: Rule { Example("switch (lhs, rhs) {}"), Example("if (f() { g() {} }) {}"), Example("if (a + f() {} == 1) {}"), + Example("if ({ true }()) {}"), + Example("if ({if i < 1 { true } else { false }}()) {}", excludeFromDocumentation: true), ], triggeringExamples: [ Example("↓if (condition) {}"), @@ -176,7 +178,7 @@ private extension ExprSyntax { private func containsTrailingClosure(_ node: Syntax) -> Bool { switch node.as(SyntaxEnum.self) { case .functionCallExpr(let node): - node.trailingClosure != nil + node.trailingClosure != nil || node.calledExpression.is(ClosureExprSyntax.self) case .sequenceExpr(let node): node.elements.contains { containsTrailingClosure(Syntax($0)) } default: false From cafed078fad538266b3e2dfeacce77f92e7fe77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 16 Nov 2024 22:54:59 +0100 Subject: [PATCH 079/260] Silence `superfluous_else` rule for availability conditions (#5857) --- CHANGELOG.md | 5 +++++ .../Rules/Style/SuperfluousElseRule.swift | 14 ++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f02dc71941..53845564f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5787](https://github.com/realm/SwiftLint/issues/5787) +* Silence `superfluous_else` rule on `if` expressions with only a single + availability condition. + [SimplyDanny](https://github.com/SimplyDanny) + [#5833](https://github.com/realm/SwiftLint/issues/5833) + * Stop triggering the `control_statement` rule on closures being directly called as conditions. [SimplyDanny](https://github.com/SimplyDanny) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift index c2d5bfffbf..d15ddd7322 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift @@ -69,6 +69,13 @@ struct SuperfluousElseRule: OptInRule { } } """), + Example(""" + if #available(iOS 13, *) { + return + } else { + deprecatedFunction() + } + """), ], triggeringExamples: [ Example(""" @@ -324,10 +331,9 @@ private extension SuperfluousElseRule { private extension IfExprSyntax { var superfluousElse: TokenSyntax? { - if elseKeyword == nil { - return nil - } - if !lastStatementExitsScope(in: body) { + guard elseKeyword != nil, + conditions.onlyElement?.condition.is(AvailabilityConditionSyntax.self) != true, + lastStatementExitsScope(in: body) else { return nil } if let parent = parent?.as(IfExprSyntax.self) { From 9e61e81748b4f14018a0f8e8fd616abac478dda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 00:00:57 +0100 Subject: [PATCH 080/260] Rework algorithm used in `function_default_parameter_at_end` rule (#5858) --- .../FunctionDefaultParameterAtEndRule.swift | 99 ++++++++----------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift index 764162b808..bbbdd4916c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift @@ -42,11 +42,14 @@ struct FunctionDefaultParameterAtEndRule: OptInRule { Example(""" func expect(file: String = #file, _ expression: @autoclosure () -> (() throws -> T)) -> Expectation {} """, excludeFromDocumentation: true), + Example("func foo(bar: Int, baz: Int = 0, z: () -> Void) {}"), + Example("func foo(bar: Int, baz: Int = 0, z: () -> Void, x: Int = 0) {}"), ], triggeringExamples: [ - Example("↓func foo(bar: Int = 0, baz: String) {}"), - Example("private ↓func foo(bar: Int = 0, baz: String) {}"), - Example("public ↓init?(for date: Date = Date(), coordinate: CLLocationCoordinate2D) {}"), + Example("func foo(↓bar: Int = 0, baz: String) {}"), + Example("private func foo(↓bar: Int = 0, baz: String) {}"), + Example("public init?(↓for date: Date = Date(), coordinate: CLLocationCoordinate2D) {}"), + Example("func foo(bar: Int, ↓baz: Int = 0, z: () -> Void, x: Int) {}"), ] ) } @@ -54,76 +57,58 @@ struct FunctionDefaultParameterAtEndRule: OptInRule { private extension FunctionDefaultParameterAtEndRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: FunctionDeclSyntax) { - guard !node.modifiers.contains(keyword: .override), node.signature.containsViolation else { - return + if !node.modifiers.contains(keyword: .override) { + collectViolations(for: node.signature) } - - violations.append(node.funcKeyword.positionAfterSkippingLeadingTrivia) } override func visitPost(_ node: InitializerDeclSyntax) { - guard !node.modifiers.contains(keyword: .override), node.signature.containsViolation else { - return + if !node.modifiers.contains(keyword: .override) { + collectViolations(for: node.signature) } - - violations.append(node.initKeyword.positionAfterSkippingLeadingTrivia) - } - } -} - -private extension FunctionSignatureSyntax { - var containsViolation: Bool { - let params = parameterClause.parameters.filter { param in - !param.isClosure } - guard params.isNotEmpty else { - return false - } - - let defaultParams = params.filter { param in - param.defaultValue != nil - } - guard defaultParams.isNotEmpty else { - return false - } - - let lastParameters = params.suffix(defaultParams.count) - let lastParametersWithDefaultValue = lastParameters.filter { param in - param.defaultValue != nil + private func collectViolations(for signature: FunctionSignatureSyntax) { + if signature.parameterClause.parameters.count < 2 { + return + } + var previousWithDefault = true + for param in signature.parameterClause.parameters.reversed() { + if param.isClosure { + continue + } + let hasDefault = param.defaultValue != nil + if !previousWithDefault, hasDefault { + violations.append(param.positionAfterSkippingLeadingTrivia) + } + previousWithDefault = hasDefault + } } - - return lastParameters.count != lastParametersWithDefaultValue.count } } private extension FunctionParameterSyntax { var isClosure: Bool { - if isEscaping || type.is(FunctionTypeSyntax.self) { - return true - } - - if let optionalType = type.as(OptionalTypeSyntax.self), - let tuple = optionalType.wrappedType.as(TupleTypeSyntax.self) { - return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil - } - - if let tuple = type.as(TupleTypeSyntax.self) { - return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil - } - - if let attrType = type.as(AttributedTypeSyntax.self) { - return attrType.baseType.is(FunctionTypeSyntax.self) - } - - return false + isEscaping || type.isFunctionType } var isEscaping: Bool { - guard let attrType = type.as(AttributedTypeSyntax.self) else { - return false - } + type.as(AttributedTypeSyntax.self)?.attributes.contains(attributeNamed: "escaping") == true + } +} - return attrType.attributes.contains(attributeNamed: "escaping") +private extension TypeSyntax { + var isFunctionType: Bool { + if `is`(FunctionTypeSyntax.self) { + true + } else if let optionalType = `as`(OptionalTypeSyntax.self) { + optionalType.wrappedType.isFunctionType + } else if let tupleType = `as`(TupleTypeSyntax.self) { + tupleType.elements.onlyElement?.type.isFunctionType == true + } else if let attributedType = `as`(AttributedTypeSyntax.self) { + attributedType.baseType.isFunctionType + } else { + false + } } } From 025b1e7f739bcef71b0cb1f6e9a0d82f07fc56de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 15:35:09 +0100 Subject: [PATCH 081/260] Allow inherited isolation parameter to be first in function signatures (#5859) --- CHANGELOG.md | 6 ++++++ .../FunctionDefaultParameterAtEndRule.swift | 21 ++++++++++++++++--- ...onDefaultParameterAtEndConfiguration.swift | 13 ++++++++++++ .../default_rule_configurations.yml | 1 + 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FunctionDefaultParameterAtEndConfiguration.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 53845564f2..3f6380f8d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,12 @@ [jaredgrubb](https://github.com/jaredgrubb) [#3750](https://github.com/realm/SwiftLint/issues/3750) +* Allow inherited isolation parameter to be first in function signatures + depending on the new option `ignore_first_isolation_inheritance_parameter` + which is `true` by default. + [SimplyDanny](https://github.com/SimplyDanny) + [#5793](https://github.com/realm/SwiftLint/issues/5793) + #### Bug Fixes * Run command plugin in whole package if no targets are defined in the diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift index bbbdd4916c..a949725fbf 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift @@ -2,7 +2,7 @@ import SwiftSyntax @SwiftSyntaxRule struct FunctionDefaultParameterAtEndRule: OptInRule { - var configuration = SeverityConfiguration(.warning) + var configuration = FunctionDefaultParameterAtEndConfiguration() static let description = RuleDescription( identifier: "function_default_parameter_at_end", @@ -44,12 +44,17 @@ struct FunctionDefaultParameterAtEndRule: OptInRule { """, excludeFromDocumentation: true), Example("func foo(bar: Int, baz: Int = 0, z: () -> Void) {}"), Example("func foo(bar: Int, baz: Int = 0, z: () -> Void, x: Int = 0) {}"), + Example("func foo(isolation: isolated (any Actor)? = #isolation, bar: String) {}"), ], triggeringExamples: [ Example("func foo(↓bar: Int = 0, baz: String) {}"), Example("private func foo(↓bar: Int = 0, baz: String) {}"), Example("public init?(↓for date: Date = Date(), coordinate: CLLocationCoordinate2D) {}"), Example("func foo(bar: Int, ↓baz: Int = 0, z: () -> Void, x: Int) {}"), + Example( + "func foo(isolation: isolated (any Actor)? = #isolation, bar: String) {}", + configuration: ["ignore_first_isolation_inheritance_parameter": false] + ), ] ) } @@ -69,16 +74,22 @@ private extension FunctionDefaultParameterAtEndRule { } private func collectViolations(for signature: FunctionSignatureSyntax) { - if signature.parameterClause.parameters.count < 2 { + let numberOfParameters = signature.parameterClause.parameters.count + if numberOfParameters < 2 { return } var previousWithDefault = true - for param in signature.parameterClause.parameters.reversed() { + for (index, param) in signature.parameterClause.parameters.reversed().enumerated() { if param.isClosure { continue } let hasDefault = param.defaultValue != nil if !previousWithDefault, hasDefault { + if index + 1 == numberOfParameters, + param.isInheritedIsolation, + configuration.ignoreFirstIsolationInheritanceParameter { + break // It's the last element anyway. + } violations.append(param.positionAfterSkippingLeadingTrivia) } previousWithDefault = hasDefault @@ -95,6 +106,10 @@ private extension FunctionParameterSyntax { var isEscaping: Bool { type.as(AttributedTypeSyntax.self)?.attributes.contains(attributeNamed: "escaping") == true } + + var isInheritedIsolation: Bool { + defaultValue?.value.as(MacroExpansionExprSyntax.self)?.macroName.text == "isolation" + } } private extension TypeSyntax { diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FunctionDefaultParameterAtEndConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FunctionDefaultParameterAtEndConfiguration.swift new file mode 100644 index 0000000000..71d4d33bbd --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FunctionDefaultParameterAtEndConfiguration.swift @@ -0,0 +1,13 @@ +import SwiftLintCore + +@AutoConfigParser +struct FunctionDefaultParameterAtEndConfiguration: SeverityBasedRuleConfiguration { + // swiftlint:disable:previous type_name + + typealias Parent = FunctionDefaultParameterAtEndRule + + @ConfigurationElement(key: "severity") + private(set) var severityConfiguration = SeverityConfiguration(.warning) + @ConfigurationElement(key: "ignore_first_isolation_inheritance_parameter") + private(set) var ignoreFirstIsolationInheritanceParameter = true +} diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index f9014e5743..bbb19d493b 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -204,6 +204,7 @@ function_body_length: error: 100 function_default_parameter_at_end: severity: warning + ignore_first_isolation_inheritance_parameter: true function_parameter_count: warning: 5 error: 8 From 2c7e7230b0af3c7160bff936e9518e83c76dcbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 15:40:39 +0100 Subject: [PATCH 082/260] Support type casting on configuration option values defined by environment variables (#5860) --- CHANGELOG.md | 6 +++ Source/SwiftLintCore/Models/YamlParser.swift | 42 +++++++----------- .../YamlParserTests.swift | 44 +++++++++++++++++++ 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6380f8d6..86b26568b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ [Martin Redington](https://github.com/mildm8nnered) [#5801](https://github.com/realm/SwiftLint/issues/5801) +* Support type casting on configuration option values defined by environment variables. + Without a cast, these values would always be treated as strings leading to a potentially + invalid configuration. + [SimplyDanny](https://github.com/SimplyDanny) + [#5774](https://github.com/realm/SwiftLint/issues/5774) + * The `redundant_type_annotation` rule gains a new option, `ignore_properties`, that skips enforcement on members in a type declaration (like a `struct`). This helps the rule coexist with diff --git a/Source/SwiftLintCore/Models/YamlParser.swift b/Source/SwiftLintCore/Models/YamlParser.swift index ab4af13f87..018478a927 100644 --- a/Source/SwiftLintCore/Models/YamlParser.swift +++ b/Source/SwiftLintCore/Models/YamlParser.swift @@ -31,40 +31,28 @@ private extension Constructor { static func customScalarMap(env: [String: String]) -> ScalarMap { var map = defaultScalarMap - map[.str] = String.constructExpandingEnvVars(env: env) - map[.bool] = Bool.constructUsingOnlyTrueAndFalse - + map[.str] = { $0.string.expandingEnvVars(env: env) } + map[.bool] = { + switch $0.string.expandingEnvVars(env: env).lowercased() { + case "true": true + case "false": false + default: nil + } + } + map[.int] = { Int($0.string.expandingEnvVars(env: env)) } + map[.float] = { Double($0.string.expandingEnvVars(env: env)) } return map } } private extension String { - static func constructExpandingEnvVars(env: [String: String]) -> (_ scalar: Node.Scalar) -> String? { - { (scalar: Node.Scalar) -> String? in - scalar.string.expandingEnvVars(env: env) - } - } - func expandingEnvVars(env: [String: String]) -> String { - var result = self - for (key, value) in env { - result = result.replacingOccurrences(of: "${\(key)}", with: value) + guard contains("${") else { + // No environment variables used. + return self } - - return result - } -} - -private extension Bool { - // swiftlint:disable:next discouraged_optional_boolean - static func constructUsingOnlyTrueAndFalse(from scalar: Node.Scalar) -> Bool? { - switch scalar.string.lowercased() { - case "true": - return true - case "false": - return false - default: - return nil + return env.reduce(into: self) { result, envVar in + result = result.replacingOccurrences(of: "${\(envVar.key)}", with: envVar.value) } } } diff --git a/Tests/SwiftLintFrameworkTests/YamlParserTests.swift b/Tests/SwiftLintFrameworkTests/YamlParserTests.swift index 4ab1322673..4e36027c76 100644 --- a/Tests/SwiftLintFrameworkTests/YamlParserTests.swift +++ b/Tests/SwiftLintFrameworkTests/YamlParserTests.swift @@ -53,4 +53,48 @@ final class YamlParserTests: SwiftLintTestCase { _ = try YamlParser.parse("|\na", env: [:]) } } + + func testTreatAllEnvVarsAsStringsWithoutCasting() throws { + let env = [ + "INT": "1", + "FLOAT": "1.0", + "BOOL": "true", + "STRING": "string", + ] + let string = """ + int: ${INT} + float: ${FLOAT} + bool: ${BOOL} + string: ${STRING} + """ + + let result = try YamlParser.parse(string, env: env) + + XCTAssertEqual(result["int"] as? String, "1") + XCTAssertEqual(result["float"] as? String, "1.0") + XCTAssertEqual(result["bool"] as? String, "true") + XCTAssertEqual(result["string"] as? String, "string") + } + + func testRespectCastsOnEnvVars() throws { + let env = [ + "INT": "1", + "FLOAT": "1.0", + "BOOL": "true", + "STRING": "string", + ] + let string = """ + int: !!int ${INT} + float: !!float ${FLOAT} + bool: !!bool ${BOOL} + string: !!str ${STRING} + """ + + let result = try YamlParser.parse(string, env: env) + + XCTAssertEqual(result["int"] as? Int, 1) + XCTAssertEqual(result["float"] as? Double, 1.0) + XCTAssertEqual(result["bool"] as? Bool, true) + XCTAssertEqual(result["string"] as? String, "string") + } } From 086f1b37e2e75155aa2f80b44b7dec0aad3d02d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 15:55:57 +0100 Subject: [PATCH 083/260] Sync build settings and options (#5861) --- Package.swift | 8 +++++--- Tests/BUILD | 13 ++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 2789c2fef6..0e85adeb96 100644 --- a/Package.swift +++ b/Package.swift @@ -90,7 +90,7 @@ let package = Package( .target( name: "SwiftLintExtraRules", dependencies: ["SwiftLintCore"], - swiftSettings: strictConcurrency + swiftSettings: swiftFeatures + strictConcurrency ), .target( name: "SwiftLintFramework", @@ -108,7 +108,8 @@ let package = Package( dependencies: [ "SwiftLintFramework" ], - path: "Tests/SwiftLintTestHelpers" + path: "Tests/SwiftLintTestHelpers", + swiftSettings: swiftFeatures ), .testTarget( name: "SwiftLintFrameworkTests", @@ -146,7 +147,8 @@ let package = Package( dependencies: [ "SwiftLintFramework", "SwiftLintTestHelpers", - ] + ], + swiftSettings: swiftFeatures ), .macro( name: "SwiftLintCoreMacros", diff --git a/Tests/BUILD b/Tests/BUILD index 01aa65395c..2c846a7625 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -2,7 +2,18 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library", "swift_test") exports_files(["BUILD"]) -copts = ["-enable-upcoming-feature", "ExistentialAny"] +copts = [ + "-enable-upcoming-feature", + "ExistentialAny", + "-enable-upcoming-feature", + "ConciseMagicFile", + "-enable-upcoming-feature", + "ImportObjcForwardDeclarations", + "-enable-upcoming-feature", + "ForwardTrailingClosures", + "-enable-upcoming-feature", + "ImplicitOpenExistentials", +] # CLITests From c784adcb034fb20d1d2445f595125206d16227c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 19 Nov 2024 22:58:57 +0100 Subject: [PATCH 084/260] Pass only remaining arguments to `swiftlint` command (#5865) Missing part of 7904d9a. --- .../SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index e1f562fd18..ec82ec5782 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -26,8 +26,9 @@ struct SwiftLintCommandPlugin: CommandPlugin { let targets = targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames) - if targets.isEmpty || !commandsNotExpectingPaths.isDisjoint(with: arguments) { - try run(with: context, arguments: arguments) + let remainingArguments = argExtractor.remainingArguments + if targets.isEmpty || !commandsNotExpectingPaths.isDisjoint(with: remainingArguments) { + try run(with: context, arguments: remainingArguments) return } for target in targets { @@ -35,7 +36,7 @@ struct SwiftLintCommandPlugin: CommandPlugin { Diagnostics.warning("Target '\(target.name)' is not a source module; skipping it") continue } - try run(in: target.directory.string, for: target.name, with: context, arguments: arguments) + try run(in: target.directory.string, for: target.name, with: context, arguments: remainingArguments) } } From 2a723d00428a441d5289459ca18ec9c2a844886b Mon Sep 17 00:00:00 2001 From: Kim de Vos Date: Sun, 24 Nov 2024 15:14:08 +0100 Subject: [PATCH 085/260] Add new option `max_number_of_single_line_parameters ` to `multiline_parameters` rule (#5781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 8 ++++ .../MultilineParametersConfiguration.swift | 25 ++++++++++++ .../Rules/Style/MultilineParametersRule.swift | 6 +++ .../MultilineParametersRuleExamples.swift | 14 +++++++ ...ultilineParametersConfigurationTests.swift | 39 +++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 86b26568b8..38ccb1a959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,14 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5774](https://github.com/realm/SwiftLint/issues/5774) +* Add new option `max_number_of_single_line_parameters` that allows only the specified maximum + number of parameters to be on one line when `allows_single_line = true`. If the limit is + exceeded, the rule will still trigger. Confusing option combinations like `allows_single_line = false` + together with `max_number_of_single_line_parameters > 1` will be reported. + [kimdv](https://github.com/kimdv) + [SimplyDanny](https://github.com/SimplyDanny) + [#5781](https://github.com/realm/SwiftLint/issues/5781) + * The `redundant_type_annotation` rule gains a new option, `ignore_properties`, that skips enforcement on members in a type declaration (like a `struct`). This helps the rule coexist with diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/MultilineParametersConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/MultilineParametersConfiguration.swift index f8705d7cb0..22c3cd470c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/MultilineParametersConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/MultilineParametersConfiguration.swift @@ -8,4 +8,29 @@ struct MultilineParametersConfiguration: SeverityBasedRuleConfiguration { private(set) var severityConfiguration = SeverityConfiguration(.warning) @ConfigurationElement(key: "allows_single_line") private(set) var allowsSingleLine = true + @ConfigurationElement(key: "max_number_of_single_line_parameters") + private(set) var maxNumberOfSingleLineParameters: Int? + + func validate() throws { + guard let maxNumberOfSingleLineParameters else { + return + } + guard maxNumberOfSingleLineParameters >= 1 else { + Issue.inconsistentConfiguration( + ruleID: Parent.identifier, + message: "Option '\($maxNumberOfSingleLineParameters.key)' should be >= 1." + ).print() + return + } + + if maxNumberOfSingleLineParameters > 1, !allowsSingleLine { + Issue.inconsistentConfiguration( + ruleID: Parent.identifier, + message: """ + Option '\($maxNumberOfSingleLineParameters.key)' has no effect when \ + '\($allowsSingleLine.key)' is false. + """ + ).print() + } + } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift index 281e97a504..72f25152c0 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift @@ -43,6 +43,12 @@ private extension MultilineParametersRule { numberOfParameters += 1 } + if let maxNumberOfSingleLineParameters = configuration.maxNumberOfSingleLineParameters, + configuration.allowsSingleLine, + numberOfParameters > maxNumberOfSingleLineParameters { + return true + } + guard linesWithParameters.count > (configuration.allowsSingleLine ? 1 : 0), numberOfParameters != linesWithParameters.count else { return false diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRuleExamples.swift index e87e35359b..4abcb60e39 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRuleExamples.swift @@ -197,6 +197,13 @@ internal struct MultilineParametersRuleExamples { ) { } } """, configuration: ["allows_single_line": false]), + Example("func foo(param1: Int, param2: Bool, param3: [String]) { }", + configuration: ["max_number_of_single_line_parameters": 3]), + Example(""" + func foo(param1: Int, + param2: Bool, + param3: [String]) { } + """, configuration: ["max_number_of_single_line_parameters": 3]), ] static let triggeringExamples: [Example] = [ @@ -336,5 +343,12 @@ internal struct MultilineParametersRuleExamples { configuration: ["allows_single_line": false]), Example("func ↓foo(param1: Int, param2: Bool, param3: [String]) { }", configuration: ["allows_single_line": false]), + Example("func ↓foo(param1: Int, param2: Bool, param3: [String]) { }", + configuration: ["max_number_of_single_line_parameters": 2]), + Example(""" + func ↓foo(param1: Int, + param2: Bool, param3: [String]) { } + """, + configuration: ["max_number_of_single_line_parameters": 3]), ] } diff --git a/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift new file mode 100644 index 0000000000..2114d6409e --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift @@ -0,0 +1,39 @@ +@testable import SwiftLintBuiltInRules +@testable import SwiftLintCore +import XCTest + +final class MultilineParametersConfigurationTests: SwiftLintTestCase { + func testInvalidMaxNumberOfSingleLineParameters() throws { + for maxNumberOfSingleLineParameters in [0, -1] { + var config = MultilineParametersConfiguration() + + XCTAssertEqual( + try Issue.captureConsole { + try config.apply( + configuration: ["max_number_of_single_line_parameters": maxNumberOfSingleLineParameters] + ) + }, + """ + warning: Inconsistent configuration for 'multiline_parameters' rule: Option \ + 'max_number_of_single_line_parameters' should be >= 1. + """ + ) + } + } + + func testInvalidMaxNumberOfSingleLineParametersWithSingleLineEnabled() throws { + var config = MultilineParametersConfiguration() + + XCTAssertEqual( + try Issue.captureConsole { + try config.apply( + configuration: ["max_number_of_single_line_parameters": 2, "allows_single_line": false] + ) + }, + """ + warning: Inconsistent configuration for 'multiline_parameters' rule: Option \ + 'max_number_of_single_line_parameters' has no effect when 'allows_single_line' is false. + """ + ) + } +} From 236c29ac2820644bf70d303eeb710b9d67d74fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 15:26:07 +0100 Subject: [PATCH 086/260] Create releases as drafts --- tools/create-github-release.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 1c9ff0cefe..7a671b9d20 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -12,11 +12,11 @@ release_notes=$(mktemp) # Create GitHub Release release_title="$(sed -n '1s/^## //p' CHANGELOG.md)" -gh release create "$version" --title "$release_title" -F "$release_notes" \ - "bazel.tar.gz" \ - "bazel.tar.gz.sha256" \ - "portable_swiftlint.zip" \ - "SwiftLint.pkg" \ +gh release create "$version" --title "$release_title" -F "$release_notes" --draft \ + "bazel.tar.gz" \ + "bazel.tar.gz.sha256" \ + "portable_swiftlint.zip" \ + "SwiftLint.pkg" \ "SwiftLintBinary-macos.artifactbundle.zip" rm "$release_notes" From 25f2776977e663305bee71309ea1e34d435065f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 16:06:56 +0100 Subject: [PATCH 087/260] Release 0.57.1 --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Package.swift | 4 ++-- Source/SwiftLintCore/Models/Version.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ccb1a959..c138090148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Main +## 0.57.1: Squeaky Clean Cycle #### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index 8ec7b30033..ff17e604d9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.57.0", + version = "0.57.1", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Package.swift b/Package.swift index 0e85adeb96..ed79593181 100644 --- a/Package.swift +++ b/Package.swift @@ -174,8 +174,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.57.0/SwiftLintBinary-macos.artifactbundle.zip", - checksum: "a1bbafe57538077f3abe4cfb004b0464dcd87e8c23611a2153c675574b858b3a" + url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary-macos.artifactbundle.zip", + checksum: "c88bf3e5bc1326d8ca66bc3f9eae786f2094c5172cd70b26b5f07686bb883899" ) ) #endif diff --git a/Source/SwiftLintCore/Models/Version.swift b/Source/SwiftLintCore/Models/Version.swift index d5a8f6e690..9d10c86ea1 100644 --- a/Source/SwiftLintCore/Models/Version.swift +++ b/Source/SwiftLintCore/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable { } /// The current SwiftLint version. - public static let current = Self(value: "0.57.0") + public static let current = Self(value: "0.57.1") /// Public initializer. /// From 554fd5647edc4bf56f56777c539049580ba34469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 16:07:33 +0100 Subject: [PATCH 088/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c138090148..67c05bad84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## Main + +#### Breaking + +* None. + +#### Experimental + +* None. + +#### Enhancements + +* None. + +#### Bug Fixes + +* None. + ## 0.57.1: Squeaky Clean Cycle #### Breaking From ece675cae9586c844cf21fa8cc770f38ca2fddb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 16:58:57 +0100 Subject: [PATCH 089/260] Do not use defined event names When this workflow is called from another, the event type will be the one of the caller. Therefore, checking agains `workflow_call` doesn't work. --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 47f6f9f583..7736846b9e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Docker tag - if: github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' + if: github.event_name != 'push' && ${{ inputs.tag }} run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - name: Use default Docker tag if: github.event_name == 'push' From 8ff2518c19848589b3ea423e978fa6855d91c4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 16:59:47 +0100 Subject: [PATCH 090/260] Use Bash <4 compatible method to UPPERCASE a string --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 51345f51b3..9f6acc511a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,8 +46,9 @@ jobs: - name: Retrieve author in uppercase id: retrieve_author run: | - AUTHOR=${{ github.event.release.author.login }} - echo "name=${AUTHOR@U}" >> $GITHUB_OUTPUT + author=${{ github.event.release.author.login }} + AUTHOR=$(echo $author | tr '[:lower:]' '[:upper:]') + echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Deploy to CocoaPods run: make pod_publish env: From e9ce8ce8bb44e1525c20c141e8e54ae1d427cc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 24 Nov 2024 17:11:28 +0100 Subject: [PATCH 091/260] Update Gems --- Gemfile.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile.lock b/Gemfile.lock index c0a88b3b3d..c67e893bb2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -154,6 +154,7 @@ PLATFORMS arm64-darwin-21 arm64-darwin-22 arm64-darwin-23 + arm64-darwin-24 x86_64-linux DEPENDENCIES From 9d9156fcc63eeb366598dd2b7134b412395d463f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 26 Nov 2024 19:58:30 +0100 Subject: [PATCH 092/260] Add Xcode command plugin (#5867) --- CHANGELOG.md | 3 +- .../CommandContext.swift | 92 +++++++++++++++++++ .../SwiftLintCommandPlugin.swift | 51 ++++++---- 3 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 Plugins/SwiftLintCommandPlugin/CommandContext.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c05bad84..afedac98c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ #### Enhancements -* None. +* Add Xcode command plugin allowing to run SwiftLint from within Xcode. + [SimplyDanny](https://github.com/SimplyDanny) #### Bug Fixes diff --git a/Plugins/SwiftLintCommandPlugin/CommandContext.swift b/Plugins/SwiftLintCommandPlugin/CommandContext.swift new file mode 100644 index 0000000000..bbe39f4640 --- /dev/null +++ b/Plugins/SwiftLintCommandPlugin/CommandContext.swift @@ -0,0 +1,92 @@ +import PackagePlugin + +protocol CommandContext { + var tool: String { get throws } + + var cacheDirectory: String { get } + + var workingDirectory: String { get } + + var unitName: String { get } + + var subUnitName: String { get } + + func targets(named names: [String]) throws -> [(paths: [String], name: String)] +} + +extension PluginContext: CommandContext { + var tool: String { + get throws { + try tool(named: "swiftlint").path.string + } + } + + var cacheDirectory: String { + pluginWorkDirectory.string + } + + var workingDirectory: String { + package.directory.string + } + + var unitName: String { + "package" + } + + var subUnitName: String { + "module" + } + + func targets(named names: [String]) throws -> [(paths: [String], name: String)] { + let targets = names.isEmpty + ? package.targets + : try package.targets(named: names) + return targets.compactMap { target in + guard let target = target.sourceModule else { + Diagnostics.warning("Target '\(target.name)' is not a source module; skipping it") + return nil + } + // Packages have a 1-to-1 mapping between targets and directories. + return (paths: [target.directory.string], name: target.name) + } + } +} + +#if canImport(XcodeProjectPlugin) + +import XcodeProjectPlugin + +extension XcodePluginContext: CommandContext { + var tool: String { + get throws { + try tool(named: "swiftlint").path.string + } + } + + var cacheDirectory: String { + pluginWorkDirectory.string + } + + var workingDirectory: String { + xcodeProject.directory.string + } + + var unitName: String { + "project" + } + + var subUnitName: String { + "target" + } + + func targets(named names: [String]) throws -> [(paths: [String], name: String)] { + if names.isEmpty { + return [(paths: [xcodeProject.directory.string], name: xcodeProject.displayName)] + } + return xcodeProject.targets + .filter { names.contains($0.displayName) } + .map { (paths: $0.inputFiles.map(\.path.string), name: $0.displayName) } + } +} + +#endif diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index ec82ec5782..21754b569d 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -17,48 +17,59 @@ private let commandsWithoutCachPathOption: Set = commandsNotExpectingPat @main struct SwiftLintCommandPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) throws { + try lintFiles(context: context, arguments: arguments) + } +} + +#if canImport(XcodeProjectPlugin) + +import XcodeProjectPlugin + +extension SwiftLintCommandPlugin: XcodeCommandPlugin { + func performCommand(context: XcodePluginContext, arguments: [String]) throws { + try lintFiles(context: context, arguments: arguments) + } +} + +#endif + +extension SwiftLintCommandPlugin { + private func lintFiles(context: some CommandContext, arguments: [String]) throws { guard !arguments.contains("--cache-path") else { Diagnostics.error("Caching is managed by the plugin and so setting `--cache-path` is not allowed") return } var argExtractor = ArgumentExtractor(arguments) let targetNames = argExtractor.extractOption(named: "target") - let targets = targetNames.isEmpty - ? context.package.targets - : try context.package.targets(named: targetNames) let remainingArguments = argExtractor.remainingArguments - if targets.isEmpty || !commandsNotExpectingPaths.isDisjoint(with: remainingArguments) { - try run(with: context, arguments: remainingArguments) + guard !targetNames.isEmpty, commandsNotExpectingPaths.isDisjoint(with: remainingArguments) else { + try lintFiles(with: context, arguments: remainingArguments) return } - for target in targets { - guard let target = target.sourceModule else { - Diagnostics.warning("Target '\(target.name)' is not a source module; skipping it") - continue - } - try run(in: target.directory.string, for: target.name, with: context, arguments: remainingArguments) + for target in try context.targets(named: targetNames) { + try lintFiles(in: target.paths, for: target.name, with: context, arguments: remainingArguments) } } - private func run(in directory: String = ".", - for targetName: String? = nil, - with context: PluginContext, - arguments: [String]) throws { + private func lintFiles(in paths: [String] = ["."], + for targetName: String? = nil, + with context: some CommandContext, + arguments: [String]) throws { let process = Process() - process.currentDirectoryURL = URL(fileURLWithPath: context.package.directory.string) - process.executableURL = URL(fileURLWithPath: try context.tool(named: "swiftlint").path.string) + process.currentDirectoryURL = URL(fileURLWithPath: context.workingDirectory) + process.executableURL = URL(fileURLWithPath: try context.tool) process.arguments = arguments if commandsWithoutCachPathOption.isDisjoint(with: arguments) { - process.arguments! += ["--cache-path", "\(context.pluginWorkDirectory.string)"] + process.arguments! += ["--cache-path", context.cacheDirectory] } if commandsNotExpectingPaths.isDisjoint(with: arguments) { - process.arguments! += [directory] + process.arguments! += paths } try process.run() process.waitUntilExit() - let module = targetName.map { "module '\($0)'" } ?? "package" + let module = targetName.map { "\(context.subUnitName) '\($0)'" } ?? context.unitName switch process.terminationReason { case .exit: Diagnostics.remark("Finished running in \(module)") From 75e5e05eef3a7a80655c76ffda0aa8294f672626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 26 Nov 2024 19:59:22 +0100 Subject: [PATCH 093/260] Require write permission for command plugin (#5870) --- CHANGELOG.md | 3 ++- Package.swift | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afedac98c6..e0c71144eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ #### Breaking -* None. +* The command plugin now requires write permissions so that it works with the `--fix` option without an error. + [SimplyDanny](https://github.com/SimplyDanny) #### Experimental diff --git a/Package.swift b/Package.swift index ed79593181..04539c6de3 100644 --- a/Package.swift +++ b/Package.swift @@ -45,7 +45,14 @@ let package = Package( ), .plugin( name: "SwiftLintCommandPlugin", - capability: .command(intent: .custom(verb: "swiftlint", description: "SwiftLint Command Plugin")), + capability: .command( + intent: .custom(verb: "swiftlint", description: "SwiftLint Command Plugin"), + permissions: [ + .writeToPackageDirectory( + reason: "When this command is run with the `--fix` option it may modify source files." + ), + ] + ), dependencies: swiftLintPluginDependencies ), .executableTarget( From b9d33e43ec8102c0de689bf33e4f2395a24835d4 Mon Sep 17 00:00:00 2001 From: fraioli Date: Tue, 26 Nov 2024 12:47:49 -0800 Subject: [PATCH 094/260] Update `file_name` rule to allow fully-qualified names of nested types (#5841) --- CHANGELOG.md | 6 ++ .../Rules/Idiomatic/FileNameRule.swift | 95 ++++++++++++++++--- .../FileNameConfiguration.swift | 2 + .../default_rule_configurations.yml | 1 + .../FileNameRuleTests.swift | 61 ++++++++---- ...Multiple.Levels.Deeply.Nested.MyType.swift | 9 ++ .../FileNameRuleFixtures/MyType.swift | 3 + .../FileNameRuleFixtures/Nested.MyType.swift | 4 + 8 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift create mode 100644 Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift create mode 100644 Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c71144eb..d109fa83bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,12 @@ [Martin Redington](https://github.com/mildm8nnered) [#5711](https://github.com/realm/SwiftLint/issues/5711) +* Fixes `file_name` rule to match fully-qualified names of nested types. + Additionally adds a `require_fully_qualified_names` boolean option to enforce + that file names match nested types only using their fully-qualified name. + [fraioli](https://github.com/fraioli) + [#5840](https://github.com/realm/SwiftLint/issues/5840) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FileNameRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FileNameRule.swift index d3554619eb..fa8999122d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FileNameRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FileNameRule.swift @@ -35,7 +35,9 @@ struct FileNameRule: OptInRule, SourceKitFreeRule { } // Process nested type separator - let allDeclaredTypeNames = TypeNameCollectingVisitor(viewMode: .sourceAccurate) + let allDeclaredTypeNames = TypeNameCollectingVisitor( + requireFullyQualifiedNames: configuration.requireFullyQualifiedNames + ) .walk(tree: file.syntaxTree, handler: \.names) .map { $0.replacingOccurrences(of: ".", with: configuration.nestedTypeSeparator) @@ -56,33 +58,96 @@ struct FileNameRule: OptInRule, SourceKitFreeRule { } private class TypeNameCollectingVisitor: SyntaxVisitor { + /// All of a visited node's ancestor type names if that node is nested, starting with the furthest + /// ancestor and ending with the direct parent + private var ancestorNames = Stack() + + /// All of the type names found in the file private(set) var names: Set = [] - override func visitPost(_ node: ClassDeclSyntax) { - names.insert(node.name.text) + /// If true, nested types are only allowed in the file name when used by their fully-qualified name + /// (e.g. `My.Nested.Type` and not just `Type`) + private let requireFullyQualifiedNames: Bool + + init(requireFullyQualifiedNames: Bool) { + self.requireFullyQualifiedNames = requireFullyQualifiedNames + super.init(viewMode: .sourceAccurate) + } + + /// Calls `visit(name:)` using the name of the provided node + private func visit(node: some NamedDeclSyntax) -> SyntaxVisitorContinueKind { + visit(name: node.name.trimmedDescription) + } + + /// Visits a node with the provided name, storing that name as an ancestor type name to prepend to + /// any children to form their fully-qualified names + private func visit(name: String) -> SyntaxVisitorContinueKind { + let fullyQualifiedName = (ancestorNames + [name]).joined(separator: ".") + names.insert(fullyQualifiedName) + + // If the options don't require only fully-qualified names, then we will allow this node's + // name to be used by itself + if !requireFullyQualifiedNames { + names.insert(name) + } + + ancestorNames.push(name) + return .visitChildren + } + + override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) + } + + override func visitPost(_: ClassDeclSyntax) { + ancestorNames.pop() + } + + override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) + } + + override func visitPost(_: ActorDeclSyntax) { + ancestorNames.pop() + } + + override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) + } + + override func visitPost(_: StructDeclSyntax) { + ancestorNames.pop() + } + + override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) + } + + override func visitPost(_: TypeAliasDeclSyntax) { + ancestorNames.pop() } - override func visitPost(_ node: ActorDeclSyntax) { - names.insert(node.name.text) + override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) } - override func visitPost(_ node: StructDeclSyntax) { - names.insert(node.name.text) + override func visitPost(_: EnumDeclSyntax) { + ancestorNames.pop() } - override func visitPost(_ node: TypeAliasDeclSyntax) { - names.insert(node.name.text) + override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { + visit(node: node) } - override func visitPost(_ node: EnumDeclSyntax) { - names.insert(node.name.text) + override func visitPost(_: ProtocolDeclSyntax) { + ancestorNames.pop() } - override func visitPost(_ node: ProtocolDeclSyntax) { - names.insert(node.name.text) + override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { + visit(name: node.extendedType.trimmedDescription) } - override func visitPost(_ node: ExtensionDeclSyntax) { - names.insert(node.extendedType.trimmedDescription) + override func visitPost(_: ExtensionDeclSyntax) { + ancestorNames.pop() } } diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileNameConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileNameConfiguration.swift index 08d414eacd..7aa8be61b5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileNameConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileNameConfiguration.swift @@ -14,4 +14,6 @@ struct FileNameConfiguration: SeverityBasedRuleConfiguration { private(set) var suffixPattern = "\\+.*" @ConfigurationElement(key: "nested_type_separator") private(set) var nestedTypeSeparator = "." + @ConfigurationElement(key: "require_fully_qualified_names") + private(set) var requireFullyQualifiedNames = false } diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index bbb19d493b..772b29252c 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -177,6 +177,7 @@ file_name: prefix_pattern: "" suffix_pattern: "\+.*" nested_type_separator: "." + require_fully_qualified_names: false file_name_no_space: severity: warning excluded: [] diff --git a/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift b/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift index 30f3167966..4e3a2923fa 100644 --- a/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift @@ -5,26 +5,33 @@ private let fixturesDirectory = "\(TestResources.path)/FileNameRuleFixtures" final class FileNameRuleTests: SwiftLintTestCase { private func validate(fileName: String, - excludedOverride: [String]? = nil, + excluded: [String]? = nil, prefixPattern: String? = nil, suffixPattern: String? = nil, - nestedTypeSeparator: String? = nil) throws -> [StyleViolation] { + nestedTypeSeparator: String? = nil, + requireFullyQualifiedNames: Bool = false) throws -> [StyleViolation] { let file = SwiftLintFile(path: fixturesDirectory.stringByAppendingPathComponent(fileName))! - let rule: FileNameRule - if let excluded = excludedOverride { - rule = try FileNameRule(configuration: ["excluded": excluded]) - } else if let prefixPattern, let suffixPattern { - rule = try FileNameRule(configuration: ["prefix_pattern": prefixPattern, "suffix_pattern": suffixPattern]) - } else if let prefixPattern { - rule = try FileNameRule(configuration: ["prefix_pattern": prefixPattern]) - } else if let suffixPattern { - rule = try FileNameRule(configuration: ["suffix_pattern": suffixPattern]) - } else if let nestedTypeSeparator { - rule = try FileNameRule(configuration: ["nested_type_separator": nestedTypeSeparator]) - } else { - rule = FileNameRule() + + var configuration = [String: Any]() + + if let excluded { + configuration["excluded"] = excluded + } + if let prefixPattern { + configuration["prefix_pattern"] = prefixPattern + } + if let suffixPattern { + configuration["suffix_pattern"] = suffixPattern + } + if let nestedTypeSeparator { + configuration["nested_type_separator"] = nestedTypeSeparator + } + if requireFullyQualifiedNames { + configuration["require_fully_qualified_names"] = requireFullyQualifiedNames } + let rule = try FileNameRule(configuration: configuration) + return rule.validate(file: file) } @@ -52,14 +59,30 @@ final class FileNameRuleTests: SwiftLintTestCase { XCTAssert(try validate(fileName: "Notification.Name+Extension.swift").isEmpty) } + func testNestedTypeDoesntTrigger() { + XCTAssert(try validate(fileName: "Nested.MyType.swift").isEmpty) + } + + func testMultipleLevelsDeeplyNestedTypeDoesntTrigger() { + XCTAssert(try validate(fileName: "Multiple.Levels.Deeply.Nested.MyType.swift").isEmpty) + } + + func testNestedTypeNotFullyQualifiedDoesntTrigger() { + XCTAssert(try validate(fileName: "MyType.swift").isEmpty) + } + + func testNestedTypeNotFullyQualifiedDoesTriggerWithOverride() { + XCTAssert(try validate(fileName: "MyType.swift", requireFullyQualifiedNames: true).isNotEmpty) + } + func testNestedTypeSeparatorDoesntTrigger() { XCTAssert(try validate(fileName: "NotificationName+Extension.swift", nestedTypeSeparator: "").isEmpty) XCTAssert(try validate(fileName: "Notification__Name+Extension.swift", nestedTypeSeparator: "__").isEmpty) } func testWrongNestedTypeSeparatorDoesTrigger() { - XCTAssert(try !validate(fileName: "Notification__Name+Extension.swift", nestedTypeSeparator: ".").isEmpty) - XCTAssert(try !validate(fileName: "NotificationName+Extension.swift", nestedTypeSeparator: "__").isEmpty) + XCTAssert(try validate(fileName: "Notification__Name+Extension.swift", nestedTypeSeparator: ".").isNotEmpty) + XCTAssert(try validate(fileName: "NotificationName+Extension.swift", nestedTypeSeparator: "__").isNotEmpty) } func testMisspelledNameDoesTrigger() { @@ -67,11 +90,11 @@ final class FileNameRuleTests: SwiftLintTestCase { } func testMisspelledNameDoesntTriggerWithOverride() { - XCTAssert(try validate(fileName: "MyStructf.swift", excludedOverride: ["MyStructf.swift"]).isEmpty) + XCTAssert(try validate(fileName: "MyStructf.swift", excluded: ["MyStructf.swift"]).isEmpty) } func testMainDoesTriggerWithoutOverride() { - XCTAssertEqual(try validate(fileName: "main.swift", excludedOverride: []).count, 1) + XCTAssertEqual(try validate(fileName: "main.swift", excluded: []).count, 1) } func testCustomSuffixPattern() { diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift new file mode 100644 index 0000000000..46bb755a47 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift @@ -0,0 +1,9 @@ +extension Multiple { + enum Levels { + class Deeply { + struct Nested { + actor MyType {} + } + } + } +} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift new file mode 100644 index 0000000000..7c72b6af9d --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift @@ -0,0 +1,3 @@ +enum Nested { + struct MyType {} +} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift new file mode 100644 index 0000000000..a57866f72a --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift @@ -0,0 +1,4 @@ +enum Nested { + struct MyType { + } +} From 0ce122e716d0042e51ed8609028e3bef19a7cfcc Mon Sep 17 00:00:00 2001 From: jkolarik-paylocity <138468976+jkolarik-paylocity@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:15:25 +0100 Subject: [PATCH 095/260] Add new `async_without_await` rule (#5869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- .swiftlint.yml | 1 + CHANGELOG.md | 4 + .../Models/BuiltInRules.swift | 1 + .../Rules/Lint/AsyncWithoutAwaitRule.swift | 156 ++++++++ .../Lint/AsyncWithoutAwaitRuleExamples.swift | 338 ++++++++++++++++++ Tests/GeneratedTests/GeneratedTests.swift | 6 + .../default_rule_configurations.yml | 2 + 7 files changed, 508 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 8dc4c564ed..59dcb36fcc 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -15,6 +15,7 @@ opt_in_rules: - all disabled_rules: - anonymous_argument_in_multiline_closure + - async_without_await - conditional_returns_on_newline - contrasted_opening_brace - convenience_type diff --git a/CHANGELOG.md b/CHANGELOG.md index d109fa83bb..10aa857e7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ * Add Xcode command plugin allowing to run SwiftLint from within Xcode. [SimplyDanny](https://github.com/SimplyDanny) + +* Add new `async_without_await` opt-in rule that checks if an `async` declaration contains at least one `await`. + [Jan Kolarik](https://github.com/jkolarik-paylocity) + [#5082](https://github.com/realm/SwiftLint/issues/5082) #### Bug Fixes diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index eeb3abce7c..075d9b8fd2 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -7,6 +7,7 @@ public let builtInRules: [any Rule.Type] = [ AccessibilityTraitForButtonRule.self, AnonymousArgumentInMultilineClosureRule.self, ArrayInitRule.self, + AsyncWithoutAwaitRule.self, AttributeNameSpacingRule.self, AttributesRule.self, BalancedXCTestLifecycleRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift new file mode 100644 index 0000000000..0604a9fc60 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift @@ -0,0 +1,156 @@ +import SwiftLintCore +import SwiftSyntax + +@SwiftSyntaxRule +struct AsyncWithoutAwaitRule: SwiftSyntaxCorrectableRule, OptInRule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "async_without_await", + name: "Async Without Await", + description: "Declaration should not be async if it doesn't use await", + kind: .lint, + nonTriggeringExamples: AsyncWithoutAwaitRuleExamples.nonTriggeringExamples, + triggeringExamples: AsyncWithoutAwaitRuleExamples.triggeringExamples, + corrections: AsyncWithoutAwaitRuleExamples.corrections + ) +} +private extension AsyncWithoutAwaitRule { + private struct FuncInfo { + var containsAwait = false + let asyncToken: TokenSyntax? + + init(asyncToken: TokenSyntax?) { + self.asyncToken = asyncToken + } + } + + final class Visitor: ViolationsSyntaxVisitor { + private var functionScopes = Stack() + private var pendingAsync: TokenSyntax? + + override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { + guard node.body != nil else { + return .visitChildren + } + + let asyncToken = node.signature.effectSpecifiers?.asyncSpecifier + functionScopes.push(.init(asyncToken: asyncToken)) + + return .visitChildren + } + + override func visitPost(_ node: FunctionDeclSyntax) { + if node.body != nil { + checkViolation() + } + } + + override func visit(_: ClosureExprSyntax) -> SyntaxVisitorContinueKind { + functionScopes.push(.init(asyncToken: pendingAsync)) + pendingAsync = nil + + return .visitChildren + } + + override func visitPost(_: ClosureExprSyntax) { + checkViolation() + } + + override func visitPost(_: AwaitExprSyntax) { + functionScopes.modifyLast { + $0.containsAwait = true + } + } + + override func visitPost(_ node: FunctionTypeSyntax) { + if let asyncNode = node.effectSpecifiers?.asyncSpecifier { + pendingAsync = asyncNode + } + } + + override func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind { + guard node.body != nil else { + return .visitChildren + } + + let asyncToken = node.effectSpecifiers?.asyncSpecifier + functionScopes.push(.init(asyncToken: asyncToken)) + + return .visitChildren + } + + override func visitPost(_ node: AccessorDeclSyntax) { + if node.body != nil { + checkViolation() + } + } + + override func visitPost(_: PatternBindingSyntax) { + pendingAsync = nil + } + + override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { + guard node.body != nil else { + return .visitChildren + } + + let asyncToken = node.signature.effectSpecifiers?.asyncSpecifier + functionScopes.push(.init(asyncToken: asyncToken)) + + return .visitChildren + } + + override func visitPost(_ node: InitializerDeclSyntax) { + if node.body != nil { + checkViolation() + } + } + + override func visitPost(_: TypeAliasDeclSyntax) { + pendingAsync = nil + } + + override func visitPost(_ node: ForStmtSyntax) { + if node.awaitKeyword != nil { + functionScopes.modifyLast { + $0.containsAwait = true + } + } + } + + override func visitPost(_ node: VariableDeclSyntax) { + if node.bindingSpecifier.tokenKind == .keyword(.let), + node.modifiers.contains(keyword: .async) { + functionScopes.modifyLast { + $0.containsAwait = true + } + } + } + + override func visit(_: FunctionParameterSyntax) -> SyntaxVisitorContinueKind { + .skipChildren + } + + override func visitPost(_: ReturnClauseSyntax) { + pendingAsync = nil + } + + private func checkViolation() { + guard let info = functionScopes.pop(), + let asyncToken = info.asyncToken, + !info.containsAwait else { + return + } + + violations.append( + at: asyncToken.positionAfterSkippingLeadingTrivia, + correction: .init( + start: asyncToken.positionAfterSkippingLeadingTrivia, + end: asyncToken.endPosition, + replacement: "" + ) + ) + } + } +} diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift new file mode 100644 index 0000000000..205dcae8fc --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift @@ -0,0 +1,338 @@ +internal struct AsyncWithoutAwaitRuleExamples { + static let nonTriggeringExamples = [ + Example(""" + func test() { + func test() async { + await test() + } + }, + """), + Example(""" + func test() { + func test() async { + await test().value + } + }, + """), + Example(""" + func test() async { + await scheduler.task { foo { bar() } } + } + """), + Example(""" + func test() async { + perform(await try foo().value) + } + """), + Example(""" + func test() async { + perform(try await foo()) + } + """), + Example(""" + func test() async { + await perform() + func baz() { + qux() + } + } + """), + Example(""" + let x: () async -> Void = { + await test() + } + """), + Example(""" + let x: () async -> Void = { + await { await test() }() + } + """), + Example(""" + func test() async { + await foo() + } + let x = { bar() } + """), + Example(""" + let x: (() async -> Void)? = { + await { await test() }() + } + """), + Example(""" + let x: (() async -> Void)? = nil + let x: () -> Void = { test() } + """), + Example(""" + var test: Int { + get async throws { + try await foo() + } + } + var foo: Int { + get throws { + try bar() + } + } + """), + Example(""" + init() async { + await foo() + } + """), + Example(""" + init() async { + func test() async { + await foo() + } + await { await foo() }() + } + """), + Example(""" + subscript(row: Int) -> Double { + get async { + await foo() + } + } + """), + Example(""" + func foo() async -> Int + func bar() async -> Int + """), + Example(""" + var foo: Int { get async } + var bar: Int { get async } + """), + Example(""" + init(foo: bar) async + init(baz: qux) async + let baz = { qux() } + """), + Example(""" + typealias Foo = () async -> Void + typealias Bar = () async -> Void + let baz = { qux() } + """), + Example(""" + func test() async { + for await foo in bar {} + } + """), + Example(""" + func test() async { + while let foo = await bar() {} + } + """), + Example(""" + func test() async { + async let foo = bar() + let baz = await foo + } + """), + Example(""" + func test() async { + async let foo = bar() + await foo + } + """), + Example(""" + func test() async { + async let foo = bar() + } + """), + Example("func foo(bar: () async -> Void) { { } }"), + Example("func foo(bar: () async -> Void = { await baz() }) { {} }"), + Example("func foo() -> (() async -> Void)? { {} }"), + Example(""" + func foo( + bar: () async -> Void, + baz: () -> Void = {} + ) { { } } + """), + Example("func foo(bar: () async -> Void = {}) { }"), + ] + + static let triggeringExamples = [ + Example(""" + func test() ↓async { + perform() + } + """), + Example(""" + func test() { + func baz() ↓async { + qux() + } + perform() + func baz() { + qux() + } + } + """), + Example(""" + func test() ↓async { + func baz() async { + await qux() + } + } + """), + Example(""" + func test() ↓async { + func foo() ↓async {} + let bar = { await foo() } + } + """), + Example(""" + func test() ↓async { + let bar = { + func foo() ↓async {} + } + } + """), + Example("let x: (() ↓async -> Void)? = { test() }"), + Example(""" + var test: Int { + get ↓async throws { + foo() + } + } + """), + Example(""" + var test: Int { + get ↓async throws { + func foo() ↓async {} + let bar = { await foo() } + } + } + """), + Example(""" + var test: Int { + get throws { + func foo() {} + let bar: () ↓async -> Void = { foo() } + } + } + """), + Example("init() ↓async {}"), + Example(""" + init() ↓async { + func foo() ↓async {} + let bar: () ↓async -> Void = { foo() } + } + """), + Example(""" + subscript(row: Int) -> Double { + get ↓async { + 1.0 + } + } + """), + Example(""" + func test() ↓async { + for foo in bar {} + } + """), + Example(""" + func test() ↓async { + while let foo = bar() {} + } + """), + ] + + static let corrections = [ + Example("func test() ↓async {}"): Example("func test() {}"), + Example("func test() ↓async throws {}"): Example("func test() throws {}"), + Example(""" + func test() { + func baz() ↓async { + qux() + } + perform() + func baz() { + qux() + } + } + """): + Example(""" + func test() { + func baz() { + qux() + } + perform() + func baz() { + qux() + } + } + """), + Example(""" + func test() ↓async{ + func baz() async { + await qux() + } + } + """): + Example(""" + func test() { + func baz() async { + await qux() + } + } + """), + Example(""" + func test() ↓async { + func foo() ↓async {} + let bar = { await foo() } + } + """): + Example(""" + func test() { + func foo() {} + let bar = { await foo() } + } + """), + Example("let x: () ↓async -> Void = { test() }"): + Example("let x: () -> Void = { test() }"), + Example(""" + var test: Int { + get ↓async throws { + func foo() ↓async {} + let bar = { await foo() } + } + } + """): + Example(""" + var test: Int { + get throws { + func foo() {} + let bar = { await foo() } + } + } + """), + Example("init() ↓async {}"): Example("init() {}"), + Example(""" + init() ↓async { + func foo() ↓async {} + let bar: () ↓async -> Void = { foo() } + } + """): + Example(""" + init() { + func foo() {} + let bar: () -> Void = { foo() } + } + """), + Example(""" + subscript(row: Int) -> Double { + get ↓async { + foo() + } + } + """): + Example(""" + subscript(row: Int) -> Double { + get { + foo() + } + } + """), + ] +} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index d00101a90b..202bd725f9 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -31,6 +31,12 @@ final class ArrayInitRuleGeneratedTests: SwiftLintTestCase { } } +final class AsyncWithoutAwaitRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(AsyncWithoutAwaitRule.description) + } +} + final class AttributeNameSpacingRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(AttributeNameSpacingRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 772b29252c..7ec4ddeae4 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -6,6 +6,8 @@ anonymous_argument_in_multiline_closure: severity: warning array_init: severity: warning +async_without_await: + severity: warning attribute_name_spacing: severity: error attributes: From daebaa31152e3f1c48785996e35cbe972832a601 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Wed, 27 Nov 2024 21:30:22 +0000 Subject: [PATCH 096/260] Include AMD64 Linux binary in `artifactbundle` (#5866) --- CHANGELOG.md | 5 +++ Dockerfile | 1 + Makefile | 35 ++++++++++++------- Package.swift | 1 + tools/create-github-release.sh | 2 +- ...macos.json.template => info.json.template} | 4 +++ tools/update-artifact-bundle.sh | 4 +-- 7 files changed, 37 insertions(+), 15 deletions(-) rename tools/{info-macos.json.template => info.json.template} (68%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10aa857e7d..9aa9ed4c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ * The command plugin now requires write permissions so that it works with the `--fix` option without an error. [SimplyDanny](https://github.com/SimplyDanny) +* The artifact bundle name has changed. `SwiftLintBinary-macos.artifactbundle.zip` is now called + `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. + [Bradley Mackey](https://github.com/bradleymackey) + [#5514](https://github.com/realm/SwiftLint/issues/5514) + #### Experimental * None. diff --git a/Dockerfile b/Dockerfile index 146675d8e6..d4b0c2f92e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ RUN swift package update ARG SWIFT_FLAGS="-c release -Xswiftc -static-stdlib -Xlinker -l_CFURLSessionInterface -Xlinker -l_CFXMLInterface -Xlinker -lcurl -Xlinker -lxml2 -Xswiftc -I. -Xlinker -fuse-ld=lld -Xlinker -L/usr/lib/swift/linux" RUN swift build $SWIFT_FLAGS --product swiftlint RUN mv `swift build $SWIFT_FLAGS --show-bin-path`/swiftlint /usr/bin +RUN strip /usr/bin/swiftlint # Runtime image FROM ${RUNTIME_IMAGE} diff --git a/Makefile b/Makefile index b20fa4e15c..b624d78cac 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ SWIFT_BUILD_FLAGS=--configuration release -Xlinker -dead_strip SWIFTLINT_EXECUTABLE_PARENT=.build/universal SWIFTLINT_EXECUTABLE=$(SWIFTLINT_EXECUTABLE_PARENT)/swiftlint +SWIFTLINT_EXECUTABLE_LINUX_PARENT=.build/linux +SWIFTLINT_EXECUTABLE_LINUX_AMD64=$(SWIFTLINT_EXECUTABLE_LINUX_PARENT)/swiftlint_linux_amd64 ARTIFACT_BUNDLE_PATH=$(TEMPORARY_FOLDER)/SwiftLintBinary.artifactbundle @@ -26,7 +28,7 @@ OUTPUT_PACKAGE=SwiftLint.pkg VERSION_STRING=$(shell ./tools/get-version) -.PHONY: all clean build install package test uninstall docs +.PHONY: all clean build build_linux install package test uninstall docs all: build @@ -78,6 +80,11 @@ build: chmod +w "$(SWIFTLINT_EXECUTABLE)" strip -rSTX "$(SWIFTLINT_EXECUTABLE)" +build_linux: + mkdir -p "$(SWIFTLINT_EXECUTABLE_LINUX_PARENT)" + docker run --platform linux/amd64 "ghcr.io/realm/swiftlint:$(VERSION_STRING)" cat /usr/bin/swiftlint > "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" + chmod +x "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" + build_with_disable_sandbox: swift build --disable-sandbox $(SWIFT_BUILD_FLAGS) @@ -93,6 +100,10 @@ installables: build install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" +installables_linux: build_linux + install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" + install "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" + prefix_install: build_with_disable_sandbox install -d "$(PREFIX)/bin/" install "$(SWIFTLINT_EXECUTABLE)" "$(PREFIX)/bin/" @@ -102,24 +113,24 @@ portable_zip: installables cp -f "$(LICENSE_PATH)" "$(TEMPORARY_FOLDER)" (cd "$(TEMPORARY_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./portable_swiftlint.zip" -spm_artifactbundle_macos: installables +spm_artifactbundle: installables installables_linux mkdir -p "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-macos/bin" - sed 's/__VERSION__/$(VERSION_STRING)/g' tools/info-macos.json.template > "$(ARTIFACT_BUNDLE_PATH)/info.json" - cp -f "$(SWIFTLINT_EXECUTABLE)" "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-macos/bin" + mkdir -p "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-linux-gnu/bin" + sed 's/__VERSION__/$(VERSION_STRING)/g' tools/info.json.template > "$(ARTIFACT_BUNDLE_PATH)/info.json" + cp -f "$(SWIFTLINT_EXECUTABLE)" "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-macos/bin/swiftlint" + cp -f "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-linux-gnu/bin/swiftlint" cp -f "$(LICENSE_PATH)" "$(ARTIFACT_BUNDLE_PATH)" - (cd "$(TEMPORARY_FOLDER)"; zip -yr - "SwiftLintBinary.artifactbundle") > "./SwiftLintBinary-macos.artifactbundle.zip" + (cd "$(TEMPORARY_FOLDER)"; zip -yr - "SwiftLintBinary.artifactbundle") > "./SwiftLintBinary.artifactbundle.zip" -zip_linux: docker_image +zip_linux: docker_image build_linux $(eval TMP_FOLDER := $(shell mktemp -d)) - docker run swiftlint cat /usr/bin/swiftlint > "$(TMP_FOLDER)/swiftlint" - chmod +x "$(TMP_FOLDER)/swiftlint" + cp -f $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) "$(TMP_FOLDER)/swiftlint" cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" (cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip" -zip_linux_release: +zip_linux_release: build_linux $(eval TMP_FOLDER := $(shell mktemp -d)) - docker run --platform linux/amd64 "ghcr.io/realm/swiftlint:$(VERSION_STRING)" cat /usr/bin/swiftlint > "$(TMP_FOLDER)/swiftlint" - chmod +x "$(TMP_FOLDER)/swiftlint" + cp -f "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TMP_FOLDER)/swiftlint" cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" (cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip" gh release upload "$(VERSION_STRING)" swiftlint_linux.zip @@ -182,7 +193,7 @@ endif make package make bazel_release make portable_zip - make spm_artifactbundle_macos + make spm_artifactbundle ./tools/update-artifact-bundle.sh "$(NEW_VERSION)" git commit -a -m "Release $(NEW_VERSION)" git tag -a $(NEW_VERSION) -m "$(NEW_VERSION_AND_NAME)" diff --git a/Package.swift b/Package.swift index 04539c6de3..85cdce9d31 100644 --- a/Package.swift +++ b/Package.swift @@ -178,6 +178,7 @@ let package = Package( ) #if os(macOS) +// TODO: in the next release the artifactbundle is not suffixed with "-macos" package.targets.append( .binaryTarget( name: "SwiftLintBinary", diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 7a671b9d20..8256ac5a6c 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -17,6 +17,6 @@ gh release create "$version" --title "$release_title" -F "$release_notes" --draf "bazel.tar.gz.sha256" \ "portable_swiftlint.zip" \ "SwiftLint.pkg" \ - "SwiftLintBinary-macos.artifactbundle.zip" + "SwiftLintBinary.artifactbundle.zip" rm "$release_notes" diff --git a/tools/info-macos.json.template b/tools/info.json.template similarity index 68% rename from tools/info-macos.json.template rename to tools/info.json.template index ef75578a64..0829f935da 100644 --- a/tools/info-macos.json.template +++ b/tools/info.json.template @@ -8,6 +8,10 @@ { "path": "swiftlint-__VERSION__-macos/bin/swiftlint", "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"] + }, + { + "path": "swiftlint-__VERSION__-linux-gnu/bin/swiftlint", + "supportedTriples": ["x86_64-unknown-linux-gnu"] } ] } diff --git a/tools/update-artifact-bundle.sh b/tools/update-artifact-bundle.sh index c98ef2b4e2..032a423757 100755 --- a/tools/update-artifact-bundle.sh +++ b/tools/update-artifact-bundle.sh @@ -3,11 +3,11 @@ set -euo pipefail readonly version="$1" -readonly artifactbundle="SwiftLintBinary-macos.artifactbundle.zip" +readonly artifactbundle="SwiftLintBinary.artifactbundle.zip" readonly checksum="$(shasum -a 256 "$artifactbundle" | cut -d " " -f1 | xargs)" sed -i '' \ - "s/.*\/releases\/download\/.*/ url: \"https:\/\/github.com\/realm\/SwiftLint\/releases\/download\/$version\/SwiftLintBinary-macos\.artifactbundle\.zip\",/g" \ + "s/.*\/releases\/download\/.*/ url: \"https:\/\/github.com\/realm\/SwiftLint\/releases\/download\/$version\/SwiftLintBinary\.artifactbundle\.zip\",/g" \ Package.swift sed -i '' \ From 172d85ab7a17b25b1e74f9ff01541dc10010a73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 29 Nov 2024 00:00:44 +0100 Subject: [PATCH 097/260] Add pre-defined internal Swift 6 version (#5872) --- .../SwiftLintCore/Models/SwiftVersion.swift | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Source/SwiftLintCore/Models/SwiftVersion.swift b/Source/SwiftLintCore/Models/SwiftVersion.swift index 27bc80c658..4918793ed1 100644 --- a/Source/SwiftLintCore/Models/SwiftVersion.swift +++ b/Source/SwiftLintCore/Models/SwiftVersion.swift @@ -45,41 +45,37 @@ extension VersionComparable { } public extension SwiftVersion { - /// Swift 5.0.x - https://swift.org/download/#swift-50 + /// Swift 5 static let five = SwiftVersion(rawValue: "5.0.0") - /// Swift 5.1.x - https://swift.org/download/#swift-51 + /// Swift 5.1 static let fiveDotOne = SwiftVersion(rawValue: "5.1.0") - /// Swift 5.2.x - https://swift.org/download/#swift-52 + /// Swift 5.2 static let fiveDotTwo = SwiftVersion(rawValue: "5.2.0") - /// Swift 5.3.x - https://swift.org/download/#swift-53 + /// Swift 5.3 static let fiveDotThree = SwiftVersion(rawValue: "5.3.0") - /// Swift 5.4.x - https://swift.org/download/#swift-54 + /// Swift 5.4 static let fiveDotFour = SwiftVersion(rawValue: "5.4.0") - /// Swift 5.5.x - https://swift.org/download/#swift-55 + /// Swift 5.5 static let fiveDotFive = SwiftVersion(rawValue: "5.5.0") - /// Swift 5.6.x - https://swift.org/download/#swift-56 + /// Swift 5.6 static let fiveDotSix = SwiftVersion(rawValue: "5.6.0") - /// Swift 5.7.x - https://swift.org/download/#swift-57 + /// Swift 5.7 static let fiveDotSeven = SwiftVersion(rawValue: "5.7.0") - /// Swift 5.8.x - https://swift.org/download/#swift-58 + /// Swift 5.8 static let fiveDotEight = SwiftVersion(rawValue: "5.8.0") - /// Swift 5.9.x - https://swift.org/download/#swift-59 + /// Swift 5.9 static let fiveDotNine = SwiftVersion(rawValue: "5.9.0") + /// Swift 6 + static let six = SwiftVersion(rawValue: "6.0.0") /// The current detected Swift compiler version, based on the currently accessible SourceKit version. /// /// - note: Override by setting the `SWIFTLINT_SWIFT_VERSION` environment variable. static let current: SwiftVersion = { - // Allow forcing the Swift version, useful in cases where SourceKit isn't available + // Allow forcing the Swift version, useful in cases where SourceKit isn't available. if let envVersion = ProcessInfo.processInfo.environment["SWIFTLINT_SWIFT_VERSION"] { - switch envVersion { - case "5": - return .five - default: - return .five - } + return SwiftVersion(rawValue: envVersion) } - if !Request.disableSourceKit { // This request was added in Swift 5.1 let params: SourceKitObject = ["key.request": UID("source.request.compiler_version")] @@ -88,7 +84,6 @@ public extension SwiftVersion { return SwiftVersion(rawValue: "\(major).\(minor).\(patch)") } } - return .five }() } From 341a5a16d2ae04a14956a37df0356315457434c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 29 Nov 2024 23:22:51 +0100 Subject: [PATCH 098/260] Disable `prefer_key_path` temporarily (#5877) Re-enable once we require Swift 6. --- .swiftlint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 59dcb36fcc..2529e986ae 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -38,6 +38,7 @@ disabled_rules: - no_grouping_extension - no_magic_numbers - one_declaration_per_file + - prefer_key_path # Re-enable once we are on Swift 6. - prefer_nimble - prefixed_toplevel_constant - required_deinit @@ -79,8 +80,6 @@ identifier_name: large_tuple: 3 number_separator: minimum_length: 5 -prefer_key_path: - restrict_to_standard_functions: false redundant_type_annotation: consider_default_literal_types_redundant: true single_test_class: *unit_test_configuration From b22c2b5c4c53401115a4a0353e9b82682fe2fd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 29 Nov 2024 23:52:37 +0100 Subject: [PATCH 099/260] Treat `compactMap` as standard function (#5876) --- .../Rules/Idiomatic/PreferKeyPathRule.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift index 127564620d..1427a250c3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift @@ -44,6 +44,7 @@ struct PreferKeyPathRule: OptInRule { Example("f.allSatisfy ↓{ (a: A) in a.b }"), Example("f.first ↓{ (a b: A) in b.c }"), Example("f.contains ↓{ $0.0.a }"), + Example("f.compactMap ↓{ $0.a.b.c.d }"), Example("f.flatMap ↓{ $0.a.b }"), Example("let f: (Int) -> Int = ↓{ $0.bigEndian }", configuration: extendedMode), ], @@ -78,6 +79,8 @@ struct PreferKeyPathRule: OptInRule { Example("f.first(where: \\.a)"), Example("f.drop ↓{ element in element.a }"): Example("f.drop(while: \\.a)"), + Example("f.compactMap ↓{ $0.a.b.c.d }"): + Example("f.compactMap(\\.a.b.c.d)"), ] ) } @@ -191,6 +194,7 @@ private extension ClosureExprSyntax { private let argumentLabelByStandardFunction: [String: String?] = [ "allSatisfy": nil, "contains": "where", + "compactMap": nil, "drop": "while", "filter": nil, "first": "where", From c45427e08b9aa7c5d1bacba3fe0562d6df8e44db Mon Sep 17 00:00:00 2001 From: Jared Grubb <1256381+jaredgrubb@users.noreply.github.com> Date: Sat, 30 Nov 2024 17:17:08 -0800 Subject: [PATCH 100/260] Handle `@unknown default` in `vertical_whitespace_between_cases` rule (#5843) --- CHANGELOG.md | 5 +++++ .../Style/VerticalWhitespaceBetweenCasesRule.swift | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aa9ed4c54..13f85c7dec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -145,6 +145,11 @@ [fraioli](https://github.com/fraioli) [#5840](https://github.com/realm/SwiftLint/issues/5840) +* Fixes an issue where the `vertical_whitespace_between_cases` rule does not + recognize `@unknown default`. + [Jared Grubb](https://github.com/jaredtrubb) + [#5788](https://github.com/realm/SwiftLint/issues/3511) + ## 0.57.0: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift index c5609af0bf..9b964d2f00 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift @@ -23,6 +23,8 @@ struct VerticalWhitespaceBetweenCasesRule: Rule { default: print("x is invalid") + @unknown default: + print("x is out of this world") } """), Example(""" @@ -42,6 +44,7 @@ struct VerticalWhitespaceBetweenCasesRule: Rule { case 0..<5: print("x is low") case 5..<10: print("x is high") default: print("x is invalid") + @unknown default: print("x is out of this world") } """), // Testing handling of trailing spaces: do not convert to """ style @@ -63,6 +66,8 @@ struct VerticalWhitespaceBetweenCasesRule: Rule { return "x is valid" ↓default: return "x is invalid" + ↓@unknown default: + print("x is out of this world") } """): Example(""" switch x { @@ -71,6 +76,9 @@ struct VerticalWhitespaceBetweenCasesRule: Rule { default: return "x is invalid" + + @unknown default: + print("x is out of this world") } """), Example(""" @@ -127,7 +135,7 @@ struct VerticalWhitespaceBetweenCasesRule: Rule { """), ] - private let pattern = "([^\\n{][ \\t]*\\n)([ \\t]*(?:case[^\\n]+|default):[ \\t]*\\n)" + private let pattern = "([^\\n{][ \\t]*\\n)([ \\t]*(?:case[^\\n]+|default|@unknown default):[ \\t]*\\n)" private func violationRanges(in file: SwiftLintFile) -> [NSRange] { file.violatingRanges(for: pattern).filter { From f13c54cf88c05b991ee2a928222bb531e660a6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 1 Dec 2024 19:30:29 +0100 Subject: [PATCH 101/260] Replace identity expressions with `\.self` (#5871) --- CHANGELOG.md | 5 +- .../Rules/Idiomatic/PreferKeyPathRule.swift | 59 +++++++++++-------- .../PreferKeyPathRuleTests.swift | 29 +++++++++ 3 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f85c7dec..5b716a67cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,14 @@ * Add Xcode command plugin allowing to run SwiftLint from within Xcode. [SimplyDanny](https://github.com/SimplyDanny) - + * Add new `async_without_await` opt-in rule that checks if an `async` declaration contains at least one `await`. [Jan Kolarik](https://github.com/jkolarik-paylocity) [#5082](https://github.com/realm/SwiftLint/issues/5082) +* Support replacing identity expressions with `\.self` in `prefer_key_path` rule from Swift 6 on. + [SimplyDanny](https://github.com/SimplyDanny) + #### Bug Fixes * None. diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift index 1427a250c3..6f3c2819f7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift @@ -19,10 +19,8 @@ struct PreferKeyPathRule: OptInRule { Example("f { $0.a }"), Example("let f = { $0.a }(b)"), Example("f {}", configuration: extendedMode), - Example("f { $0 }", configuration: extendedMode), Example("f() { g() }", configuration: extendedMode), Example("f { a.b.c }", configuration: extendedMode), - Example("f { a in a }", configuration: extendedMode), Example("f { a, b in a.b }", configuration: extendedMode), Example("f { (a, b) in a.b }", configuration: extendedMode), Example("f { $0.a } g: { $0.b }", configuration: extendedMode), @@ -30,6 +28,7 @@ struct PreferKeyPathRule: OptInRule { Example("f.map(1) { $0.a }"), Example("f.filter({ $0.a }, x)"), Example("#Predicate { $0.a }"), + Example("let transform: (Int) -> Int = nil ?? { $0.a }"), ], triggeringExamples: [ Example("f.map ↓{ $0.a }"), @@ -47,6 +46,7 @@ struct PreferKeyPathRule: OptInRule { Example("f.compactMap ↓{ $0.a.b.c.d }"), Example("f.flatMap ↓{ $0.a.b }"), Example("let f: (Int) -> Int = ↓{ $0.bigEndian }", configuration: extendedMode), + Example("transform = ↓{ $0.a }"), ], corrections: [ Example("f.map { $0.a }"): @@ -91,7 +91,9 @@ private extension PreferKeyPathRule { if node.isInvalid(restrictToStandardFunctions: configuration.restrictToStandardFunctions) { return } - if node.onlyExprStmt?.accesses(identifier: node.onlyParameter) == true { + if let onlyStmt = node.onlyExprStmt, + SwiftVersion.current >= .six || !onlyStmt.is(DeclReferenceExprSyntax.self), + onlyStmt.accesses(identifier: node.onlyParameter) { violations.append(node.positionAfterSkippingLeadingTrivia) } } @@ -104,7 +106,7 @@ private extension PreferKeyPathRule { !closure.isInvalid(restrictToStandardFunctions: configuration.restrictToStandardFunctions), let expr = closure.onlyExprStmt, expr.accesses(identifier: closure.onlyParameter) == true, - let declName = expr.as(MemberAccessExprSyntax.self), + let replacement = expr.asKeyPath, let calleeName = node.calleeName else { return super.visit(node) } @@ -115,7 +117,7 @@ private extension PreferKeyPathRule { } let newArg = LabeledExprSyntax( label: argumentLabelByStandardFunction[calleeName, default: nil], - expression: declName.asKeyPath + expression: replacement ) node = node.with(\.arguments, [newArg] ) @@ -134,9 +136,9 @@ private extension PreferKeyPathRule { } if let expr = node.onlyExprStmt, expr.accesses(identifier: node.onlyParameter) == true, - let declName = expr.as(MemberAccessExprSyntax.self) { + let replacement = expr.asKeyPath { correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - let node = declName.asKeyPath + let node = replacement .with(\.leadingTrivia, node.leadingTrivia) .with(\.trailingTrivia, node.trailingTrivia) return super.visit(node) @@ -149,11 +151,11 @@ private extension PreferKeyPathRule { private extension ExprSyntax { func accesses(identifier: String?) -> Bool { if let base = `as`(MemberAccessExprSyntax.self)?.base { - if let declRef = base.as(DeclReferenceExprSyntax.self) { - return declRef.baseName.text == identifier ?? "$0" - } return base.accesses(identifier: identifier) } + if let declRef = `as`(DeclReferenceExprSyntax.self) { + return declRef.baseName.text == identifier ?? "$0" + } return false } } @@ -179,15 +181,20 @@ private extension ClosureExprSyntax { func isInvalid(restrictToStandardFunctions: Bool) -> Bool { guard keyPathInParent != \FunctionCallExprSyntax.calledExpression, - let parentKind = parent?.kind, - ![.macroExpansionExpr, .multipleTrailingClosureElement].contains(parentKind) else { + let parent, + ![.macroExpansionExpr, .multipleTrailingClosureElement].contains(parent.kind), + previousToken(viewMode: .sourceAccurate)?.text != "??" else { return true } - if let call = parent?.as(LabeledExprSyntax.self)?.parent?.parent?.as(FunctionCallExprSyntax.self) { + if let call = parent.as(LabeledExprSyntax.self)?.parent?.parent?.as(FunctionCallExprSyntax.self) { // Closure is function argument. return restrictToStandardFunctions && !call.isStandardFunction } - return parent?.as(FunctionCallExprSyntax.self)?.isStandardFunction == false + if let call = parent.as(FunctionCallExprSyntax.self) { + // Trailing closure. + return call.additionalTrailingClosures.isNotEmpty || restrictToStandardFunctions && !call.isStandardFunction + } + return false } } @@ -218,16 +225,22 @@ private extension FunctionCallExprSyntax { } } -private extension MemberAccessExprSyntax { - var asKeyPath: ExprSyntax { - var this = base - var elements = [declName] - while this?.is(DeclReferenceExprSyntax.self) != true { - if let memberAccess = this?.as(MemberAccessExprSyntax.self) { - elements.append(memberAccess.declName) - this = memberAccess.base +private extension ExprSyntax { + var asKeyPath: ExprSyntax? { + if let memberAccess = `as`(MemberAccessExprSyntax.self) { + var this = memberAccess.base + var elements = [memberAccess.declName] + while this?.is(DeclReferenceExprSyntax.self) != true { + if let memberAccess = this?.as(MemberAccessExprSyntax.self) { + elements.append(memberAccess.declName) + this = memberAccess.base + } } + return "\\.\(raw: elements.reversed().map(\.baseName.text).joined(separator: "."))" as ExprSyntax + } + if SwiftVersion.current >= .six, `is`(DeclReferenceExprSyntax.self) { + return "\\.self" } - return "\\.\(raw: elements.reversed().map(\.baseName.text).joined(separator: "."))" as ExprSyntax + return nil } } diff --git a/Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift b/Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift new file mode 100644 index 0000000000..4b99289fc0 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift @@ -0,0 +1,29 @@ +@testable import SwiftLintBuiltInRules +import XCTest + +final class PreferKeyPathRuleTests: SwiftLintTestCase { + private static let extendedMode = ["restrict_to_standard_functions": false] + + func testIdentityExpressionInSwift6() throws { + try XCTSkipIf(SwiftVersion.current < .six) + + let description = PreferKeyPathRule.description + .with(nonTriggeringExamples: [ + Example("f.filter { a in b }"), + Example("f.g { $1 }", configuration: Self.extendedMode), + ]) + .with(triggeringExamples: [ + Example("f.compactMap ↓{ $0 }"), + Example("f.map ↓{ a in a }"), + Example("f.g { $0 }", configuration: Self.extendedMode), + ]) + .with(corrections: [ + Example("f.map ↓{ $0 }"): + Example("f.map(\\.self)"), + Example("f.g { $0 }", configuration: Self.extendedMode): + Example("f.g(\\.self)"), + ]) + + verifyRule(description) + } +} From 7dd8e65d4f704549fd2102b65fc182f3a39264d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 5 Dec 2024 22:38:34 +0100 Subject: [PATCH 102/260] Ignore super calls with trailing closures (#5887) --- CHANGELOG.md | 4 +++- .../Rules/Lint/UnneededOverrideRule.swift | 5 +++++ .../Lint/UnneededOverrideRuleExamples.swift | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b716a67cf..d29a15dc0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,9 @@ #### Bug Fixes -* None. +* Ignore super calls with trailing closures in `unneeded_override` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5886](ttps://github.com/realm/SwiftLint/issues/5886) ## 0.57.1: Squeaky Clean Cycle diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift index 3a00e4e22f..c9414ff370 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift @@ -101,6 +101,11 @@ private extension OverridableDecl { return false } + guard call.trailingClosure == nil, call.additionalTrailingClosures.isEmpty else { + // Assume trailing closures change behavior. + return false + } + let declParameters = signature.parameterClause.parameters if declParameters.contains(where: { $0.defaultValue != nil }) { // Any default parameter might be a change to the super. diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift index 87f0e94947..6cdae66cfc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRuleExamples.swift @@ -121,6 +121,22 @@ struct UnneededOverrideRuleExamples { } } """), + Example(""" + class C { + override func foo() { + super.foo {} + } + override func bar(_ c: () -> Void) { + super.bar {} + } + override func baz(_ c: () -> Void) { + super.baz({}) + } + override func qux(c: () -> Void) { + super.qux(c: {}) + } + } + """), ] static let triggeringExamples = [ From 40f9a2a18ec2470d81385e4b3f927982c424026b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Dec 2024 15:25:29 +0100 Subject: [PATCH 103/260] Remove deprecated rule entirely (#5889) The rule has been deprecated for 2 years. --- .../Models/BuiltInRules.swift | 1 - .../Rules/Lint/UnusedCaptureListRule.swift | 233 ------------------ Tests/GeneratedTests/GeneratedTests.swift | 6 - .../default_rule_configurations.yml | 2 - 4 files changed, 242 deletions(-) delete mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 075d9b8fd2..2527ee1d79 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -224,7 +224,6 @@ public let builtInRules: [any Rule.Type] = [ UnneededSynthesizedInitializerRule.self, UnownedVariableCaptureRule.self, UntypedErrorInCatchRule.self, - UnusedCaptureListRule.self, UnusedClosureParameterRule.self, UnusedControlFlowLabelRule.self, UnusedDeclarationRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift deleted file mode 100644 index 445aba6edf..0000000000 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedCaptureListRule.swift +++ /dev/null @@ -1,233 +0,0 @@ -import SwiftSyntax - -// TODO: [12/22/2024] Remove deprecation warning after ~2 years. -private let warnDeprecatedOnceImpl: Void = { - Issue.ruleDeprecated(ruleID: UnusedCaptureListRule.identifier).print() -}() - -private func warnDeprecatedOnce() { - _ = warnDeprecatedOnceImpl -} - -struct UnusedCaptureListRule: SwiftSyntaxRule, OptInRule { - var configuration = SeverityConfiguration(.warning) - - static let description = RuleDescription( - identifier: "unused_capture_list", - name: "Unused Capture List", - description: "Unused reference in a capture list should be removed", - kind: .lint, - nonTriggeringExamples: [ - Example(""" - [1, 2].map { - [ weak - delegate, - unowned - self - ] num in - delegate.handle(num) - } - """), - Example(""" - [1, 2].map { [weak self] num in - self?.handle(num) - } - """), - Example(""" - let failure: Failure = { [weak self, unowned delegate = self.delegate!] foo in - delegate.handle(foo, self) - } - """), - Example(""" - numbers.forEach({ - [weak handler] in - handler?.handle($0) - }) - """), - Example(""" - withEnvironment(apiService: MockService(fetchProjectResponse: project)) { - [Device.phone4_7inch, Device.phone5_8inch, Device.pad].forEach { device in - device.handle() - } - } - """), - Example("{ [foo] _ in foo.bar() }()"), - Example("sizes.max().flatMap { [(offset: offset, size: $0)] } ?? []"), - Example(""" - [1, 2].map { [self] num in - handle(num) - } - """), - Example(""" - [1, 2].map { [unowned self] num in - handle(num) - } - """), - Example(""" - [1, 2].map { [self, unowned delegate = self.delegate!] num in - delegate.handle(num) - } - """), - Example(""" - [1, 2].map { [unowned self, unowned delegate = self.delegate!] num in - delegate.handle(num) - } - """), - Example(""" - [1, 2].map { - [ weak - delegate, - self - ] num in - delegate.handle(num) - } - """), - Example(""" - rx.onViewDidAppear.subscribe(onNext: { [unowned self] in - doSomething() - }).disposed(by: disposeBag) - """), - Example(""" - let closure = { [weak self] in - guard let self else { - return - } - someInstanceFunction() - } - """), - ], - triggeringExamples: [ - Example(""" - [1, 2].map { [↓weak self] num in - print(num) - } - """), - Example(""" - let failure: Failure = { [weak self, ↓unowned delegate = self.delegate!] foo in - self?.handle(foo) - } - """), - Example(""" - let failure: Failure = { [↓weak self, ↓unowned delegate = self.delegate!] foo in - print(foo) - } - """), - Example(""" - numbers.forEach({ - [weak handler] in - print($0) - }) - """), - Example(""" - numbers.forEach({ - [self, ↓weak handler] in - print($0) - }) - """), - Example(""" - withEnvironment(apiService: MockService(fetchProjectResponse: project)) { [↓foo] in - [Device.phone4_7inch, Device.phone5_8inch, Device.pad].forEach { device in - device.handle() - } - } - """), - Example("{ [↓foo] in _ }()"), - Example(""" - let closure = { [↓weak a] in - // The new `a` immediately shadows the captured `a` which thus isn't needed. - guard let a = getOptionalValue() else { - return - } - someInstanceFunction() - } - """), - ] - ) - - func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor { - warnDeprecatedOnce() - return Visitor(configuration: configuration, file: file) - } -} - -private extension UnusedCaptureListRule { - final class Visitor: ViolationsSyntaxVisitor { - override func visitPost(_ node: ClosureExprSyntax) { - guard let captureItems = node.signature?.capture?.items, - captureItems.isNotEmpty else { - return - } - - let captureItemsWithNames = captureItems - .compactMap { item -> (name: String, item: ClosureCaptureSyntax)? in - if let name = item.name { - return (name.text, item) - } - if let expr = item.expression.as(DeclReferenceExprSyntax.self) { - // allow "[unowned self]" - if expr.baseName.tokenKind == .keyword(.self), - item.specifier?.specifier.tokenKind == .keyword(.unowned) { - return nil - } - - // allow "[self]" capture (SE-0269) - if expr.baseName.tokenKind == .keyword(.self), - item.specifier == nil { - return nil - } - - return (expr.baseName.text, item) - } - - return nil - } - - guard captureItemsWithNames.isNotEmpty else { - return - } - - let identifiersToSearch = Set(captureItemsWithNames.map(\.name)) - let foundIdentifiers = IdentifierReferenceVisitor(identifiersToSearch: identifiersToSearch) - .walk(tree: node.statements, handler: \.foundIdentifiers) - - let missingIdentifiers = identifiersToSearch.subtracting(foundIdentifiers) - guard missingIdentifiers.isNotEmpty else { - return - } - - for entry in captureItemsWithNames where missingIdentifiers.contains(entry.name) { - violations.append(entry.item.positionAfterSkippingLeadingTrivia) - } - } - } -} - -private final class IdentifierReferenceVisitor: SyntaxVisitor { - private let identifiersToSearch: Set - private(set) var foundIdentifiers: Set = [] - - init(identifiersToSearch: Set) { - self.identifiersToSearch = identifiersToSearch - super.init(viewMode: .sourceAccurate) - } - - override func visitPost(_ node: DeclReferenceExprSyntax) { - collectReference(by: node.baseName) - } - - override func visitPost(_ node: IdentifierPatternSyntax) { - // Optional bindings without an initializer like `if let self { ... }` reference the captured `self` - // implicitly. This is handled in here. If we have `if let self = self { ... }` the initializer `self` - // is handled in the `IdentifierExprSyntax` case above. - if let binding = node.parent?.as(OptionalBindingConditionSyntax.self), binding.initializer == nil { - collectReference(by: node.identifier) - } - } - - private func collectReference(by token: TokenSyntax) { - let name = token.text - if identifiersToSearch.contains(name) { - foundIdentifiers.insert(name) - } - } -} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 202bd725f9..14f1c866ad 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -1333,12 +1333,6 @@ final class UntypedErrorInCatchRuleGeneratedTests: SwiftLintTestCase { } } -final class UnusedCaptureListRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(UnusedCaptureListRule.description) - } -} - final class UnusedClosureParameterRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(UnusedClosureParameterRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 7ec4ddeae4..835a0743a4 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -601,8 +601,6 @@ unowned_variable_capture: severity: warning untyped_error_in_catch: severity: warning -unused_capture_list: - severity: warning unused_closure_parameter: severity: warning unused_control_flow_label: From 75c13228f0fc920292fe410f0d98df49d6595832 Mon Sep 17 00:00:00 2001 From: Danila Shikulin <92162084+dshikulin-mwb@users.noreply.github.com> Date: Sat, 7 Dec 2024 16:50:28 +0200 Subject: [PATCH 104/260] Support linting only provided file paths with command plugins (#5879) This enables anyone to run SwiftLint on file paths provided via arguments without installing it via Mint or Homebrew. This can be useful for custom Git hooks that run only for modified files. --- CHANGELOG.md | 3 +++ .../SwiftLintCommandPlugin.swift | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d29a15dc0e..b7e6fe9893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ * Support replacing identity expressions with `\.self` in `prefer_key_path` rule from Swift 6 on. [SimplyDanny](https://github.com/SimplyDanny) + +* Support linting only provided file paths with command plugins. + [DanSkeel](https://github.com/DanSkeel) #### Bug Fixes diff --git a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift index 21754b569d..fb208c4d07 100644 --- a/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift +++ b/Plugins/SwiftLintCommandPlugin/SwiftLintCommandPlugin.swift @@ -42,10 +42,20 @@ extension SwiftLintCommandPlugin { var argExtractor = ArgumentExtractor(arguments) let targetNames = argExtractor.extractOption(named: "target") let remainingArguments = argExtractor.remainingArguments - guard !targetNames.isEmpty, commandsNotExpectingPaths.isDisjoint(with: remainingArguments) else { + + if !commandsNotExpectingPaths.isDisjoint(with: remainingArguments) { try lintFiles(with: context, arguments: remainingArguments) return } + guard !targetNames.isEmpty else { + if let pathArgument = remainingArguments.last, FileManager.default.fileExists(atPath: pathArgument) { + Diagnostics.remark("No targets provided. Files provided in path arguments will be linted.") + try lintFiles(in: [], with: context, arguments: remainingArguments) + } else { + try lintFiles(with: context, arguments: remainingArguments) + } + return + } for target in try context.targets(named: targetNames) { try lintFiles(in: target.paths, for: target.name, with: context, arguments: remainingArguments) } From e42ee1090a506ed3518873a254a90ce1c1385d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 7 Dec 2024 17:32:49 +0100 Subject: [PATCH 105/260] Pass only Swift files in Xcode target to linter (#5890) --- Plugins/SwiftLintCommandPlugin/CommandContext.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/SwiftLintCommandPlugin/CommandContext.swift b/Plugins/SwiftLintCommandPlugin/CommandContext.swift index bbe39f4640..483f27d053 100644 --- a/Plugins/SwiftLintCommandPlugin/CommandContext.swift +++ b/Plugins/SwiftLintCommandPlugin/CommandContext.swift @@ -85,7 +85,7 @@ extension XcodePluginContext: CommandContext { } return xcodeProject.targets .filter { names.contains($0.displayName) } - .map { (paths: $0.inputFiles.map(\.path.string), name: $0.displayName) } + .map { (paths: $0.inputFiles.map(\.path.string).filter { $0.hasSuffix(".swift") }, name: $0.displayName) } } } From eff0c9ecd1d479f6796f1a96d8e83eaeda5bb7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 9 Dec 2024 22:40:49 +0100 Subject: [PATCH 106/260] Update involvement state of the project --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 39b7294f7e..1942fefb94 100644 --- a/README.md +++ b/README.md @@ -1029,17 +1029,18 @@ parameter.** ## About - +SwiftLint is utterly maintained by volunteers contributing to its success +entirely in their free time. As such, SwiftLint isn't a commercial product +in any way. -SwiftLint is maintained and funded by Realm Inc. The names and logos for -Realm are trademarks of Realm Inc. +Be kind to the people maintaining SwiftLint as a hobby and accept that their +time is limited. Support them by contributing to the project, reporting issues, +and helping others in the community. -We :heart: open source software! -See [our other open source projects](https://github.com/realm), -read [our blog](https://realm.io/news), or say hi on twitter -([@realm](https://twitter.com/realm)). +Special thanks go to [MacStadium](https://www.macstadium.com) for providing +physical Mac mini machines to run our performance tests. -Our thanks to MacStadium for providing a Mac Mini to run our performance -tests. +We also thank Realm (now MongoDB) for their inital contributions and setup of +the project. From f2175c190614a83bcddb7e0bd6b4a1dcde893e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 10 Dec 2024 00:30:22 +0100 Subject: [PATCH 107/260] Remove deprecated `inert_defer` rule entirely (#5894) --- CHANGELOG.md | 3 + .../Models/BuiltInRules.swift | 1 - .../Rules/Lint/InertDeferRule.swift | 116 ------------------ Tests/GeneratedTests/GeneratedTests.swift | 6 - .../default_rule_configurations.yml | 2 - 5 files changed, 3 insertions(+), 125 deletions(-) delete mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b7e6fe9893..dbc9656f8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ [Bradley Mackey](https://github.com/bradleymackey) [#5514](https://github.com/realm/SwiftLint/issues/5514) +* The `inert_defer` and `unused_capture_list` rules have completely been removed after being deprecated for 2 years. + [SimplyDanny](https://github.com/SimplyDanny) + #### Experimental * None. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 2527ee1d79..b93b94e752 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -92,7 +92,6 @@ public let builtInRules: [any Rule.Type] = [ ImplicitlyUnwrappedOptionalRule.self, InclusiveLanguageRule.self, IndentationWidthRule.self, - InertDeferRule.self, InvalidSwiftLintCommandRule.self, IsDisjointRule.self, JoinedDefaultParameterRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift deleted file mode 100644 index 87a8775ebd..0000000000 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/InertDeferRule.swift +++ /dev/null @@ -1,116 +0,0 @@ -import SwiftSyntax - -// TODO: [12/23/2024] Remove deprecation warning after ~2 years. -private let warnDeprecatedOnceImpl: Void = { - Issue.ruleDeprecated(ruleID: InertDeferRule.identifier).print() -}() - -private func warnDeprecatedOnce() { - _ = warnDeprecatedOnceImpl -} - -struct InertDeferRule: SwiftSyntaxRule, OptInRule { - var configuration = SeverityConfiguration(.warning) - - static let description = RuleDescription( - identifier: "inert_defer", - name: "Inert Defer", - description: "If defer is at the end of its parent scope, it will be executed right where it is anyway", - kind: .lint, - nonTriggeringExamples: [ - Example(""" - func example3() { - defer { /* deferred code */ } - - print("other code") - } - """), - Example(""" - func example4() { - if condition { - defer { /* deferred code */ } - print("other code") - } - } - """), - Example(""" - func f() { - #if os(macOS) - defer { print(2) } - #else - defer { print(3) } - #endif - print(1) - } - """, excludeFromDocumentation: true), - ], - triggeringExamples: [ - Example(""" - func example0() { - ↓defer { /* deferred code */ } - } - """), - Example(""" - func example1() { - ↓defer { /* deferred code */ } - // comment - } - """), - Example(""" - func example2() { - if condition { - ↓defer { /* deferred code */ } - // comment - } - } - """), - Example(""" - func f(arg: Int) { - if arg == 1 { - ↓defer { print(2) } - // a comment - } else { - ↓defer { print(3) } - } - print(1) - #if os(macOS) - ↓defer { print(4) } - #else - ↓defer { print(5) } - #endif - } - """, excludeFromDocumentation: true), - ] - ) - - func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor { - warnDeprecatedOnce() - return Visitor(configuration: configuration, file: file) - } -} - -private extension InertDeferRule { - final class Visitor: ViolationsSyntaxVisitor { - override func visitPost(_ node: DeferStmtSyntax) { - guard node.isLastStatement else { - return - } - if let ifConfigClause = node.parent?.parent?.parent?.as(IfConfigClauseSyntax.self), - ifConfigClause.parent?.parent?.isLastStatement == false { - return - } - - violations.append(node.deferKeyword.positionAfterSkippingLeadingTrivia) - } - } -} - -private extension SyntaxProtocol { - var isLastStatement: Bool { - if let codeBlockItem = parent?.as(CodeBlockItemSyntax.self), - let codeBlockList = codeBlockItem.parent?.as(CodeBlockItemListSyntax.self) { - return codeBlockList.last == codeBlockItem - } - return false - } -} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 14f1c866ad..a67ea93b77 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -541,12 +541,6 @@ final class IndentationWidthRuleGeneratedTests: SwiftLintTestCase { } } -final class InertDeferRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(InertDeferRule.description) - } -} - final class InvalidSwiftLintCommandRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(InvalidSwiftLintCommandRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 835a0743a4..3a35616033 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -255,8 +255,6 @@ indentation_width: include_comments: true include_compiler_directives: true include_multiline_strings: true -inert_defer: - severity: warning invalid_swiftlint_command: severity: warning is_disjoint: From 7c895d2781aded356f8b0d9705f997f20e2416a2 Mon Sep 17 00:00:00 2001 From: Jaeyoung Heo Date: Tue, 10 Dec 2024 18:47:27 +0900 Subject: [PATCH 108/260] Update pre-commit-hook version (#5895) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1942fefb94..7fee6e124e 100644 --- a/README.md +++ b/README.md @@ -570,7 +570,7 @@ Once [installed](https://pre-commit.com/#install), add this to the ```yaml repos: - repo: https://github.com/realm/SwiftLint - rev: 0.50.3 + rev: 0.57.1 hooks: - id: swiftlint ``` @@ -582,7 +582,7 @@ SwiftLint can be configured using `entry` to apply fixes and fail on errors: ```yaml - repo: https://github.com/realm/SwiftLint - rev: 0.50.3 + rev: 0.57.1 hooks: - id: swiftlint entry: swiftlint --fix --strict From 191bb64d75295df779546ebd4fdc8878cb37d946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 14 Dec 2024 22:33:59 +0100 Subject: [PATCH 109/260] Support Swift version 6.0.3 (#5896) --- Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift index aff4993cc8..47180590d4 100644 --- a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift +++ b/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift @@ -3,7 +3,9 @@ import XCTest final class SwiftVersionTests: SwiftLintTestCase { func testDetectSwiftVersion() { -#if compiler(>=6.0.2) +#if compiler(>=6.0.3) + let version = "6.0.3" +#elseif compiler(>=6.0.2) let version = "6.0.2" #elseif compiler(>=6.0.1) let version = "6.0.1" From 8e3b50fa61c4a87badc2c4e82b15071ef07c7970 Mon Sep 17 00:00:00 2001 From: Dinesh Sharma <65244911+dk-talks@users.noreply.github.com> Date: Sun, 15 Dec 2024 04:32:54 +0530 Subject: [PATCH 110/260] Add new category for `@IBSegueAction` to `type_contents_order` rule (#5524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 4 ++++ .../Rules/Lint/UnusedDeclarationRule.swift | 1 + .../TypeContentsOrderConfiguration.swift | 1 + .../Rules/Style/TypeContentsOrderRule.swift | 3 +++ ...SwiftDeclarationAttributeKind+Swiftlint.swift | 1 + .../TypeContentsOrderRuleTests.swift | 16 ++++++++++++++++ 6 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc9656f8a..a67a377089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,10 @@ * Support linting only provided file paths with command plugins. [DanSkeel](https://github.com/DanSkeel) +* Add new category for `@IBSegueAction` to `type_contents_order` rule. + [dk-talks](https://github.com/dk-talks) + [SimplyDanny](https://github.com/SimplyDanny) + #### Bug Fixes * Ignore super calls with trailing closures in `unneeded_override` rule. diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift index 33e3d23556..616725f30a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedDeclarationRule.swift @@ -211,6 +211,7 @@ private extension SwiftLintFile { private func shouldIgnoreEntity(_ indexEntity: SourceKittenDictionary, relatedUSRsToSkip: Set) -> Bool { let declarationAttributesToSkip: Set = [ + .ibsegueaction, .ibaction, .main, .nsApplicationMain, diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift index 01f1427f6e..9587d948b6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift @@ -17,6 +17,7 @@ enum TypeContent: String { case otherMethod = "other_method" case `subscript` = "subscript" case deinitializer = "deinitializer" + case ibSegueAction = "ib_segue_action" } @AutoConfigParser diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/TypeContentsOrderRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/TypeContentsOrderRule.swift index daae7c6252..a941e43751 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/TypeContentsOrderRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/TypeContentsOrderRule.swift @@ -133,6 +133,9 @@ struct TypeContentsOrderRule: OptInRule { if typeContentStructure.enclosedSwiftAttributes.contains(SwiftDeclarationAttributeKind.ibaction) { return .ibAction } + if typeContentStructure.enclosedSwiftAttributes.contains(SwiftDeclarationAttributeKind.ibsegueaction) { + return .ibSegueAction + } return .otherMethod case .functionSubscript: diff --git a/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift b/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift index 26d2f2ef5b..49895e6e80 100644 --- a/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift +++ b/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift @@ -85,6 +85,7 @@ public extension SwiftDeclarationAttributeKind { .nonobjc, .objcMembers, .ibaction, + .ibsegueaction, .iboutlet, .ibdesignable, .ibinspectable, diff --git a/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift b/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift index f79efb7ab9..808e3bb41d 100644 --- a/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift @@ -277,6 +277,10 @@ final class TypeContentsOrderRuleTests: SwiftLintTestCase { view2.layoutIfNeeded() hasLayoutedView2 = true } + + @IBSegueAction func prepareForNextVc(_ coder: NSCoder) -> UIViewController? { + getRandomVc() + } } """), ] @@ -330,6 +334,17 @@ final class TypeContentsOrderRuleTests: SwiftLintTestCase { } } """), + Example(""" + class C { + func f() {} + + @IBSegueAction ↓func foo(_ coder: NSCoder) -> UIViewController? { + nil + } + + @IBAction func bar() {} + } + """), ] let groupedOrderDescription = TypeContentsOrderRule.description @@ -344,6 +359,7 @@ final class TypeContentsOrderRuleTests: SwiftLintTestCase { ["type_property", "instance_property", "ib_inspectable", "ib_outlet"], ["initializer", "type_method", "deinitializer"], ["view_life_cycle_method", "ib_action", "other_method", "subscript"], + ["ib_segue_action"], ], ] ) From 23ba688c88cdcdbf415bc2a7c175187d55507091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 15 Dec 2024 23:20:18 +0100 Subject: [PATCH 111/260] Combine compact and flat mapping (#5897) --- .../Models/RuleConfigurationDescription.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift index b0e5750897..c3636b3c29 100644 --- a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift +++ b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift @@ -53,16 +53,14 @@ public struct RuleConfigurationDescription: Equatable { return Self(options: customDescription.options, exclusiveOptions: exclusiveOptions) } let options: [RuleConfigurationOption] = Mirror(reflecting: configuration).children - .compactMap { child -> Self? in + .flatMap { child in // Property wrappers have names prefixed by an underscore. - guard let codingKey = child.label, codingKey.starts(with: "_") else { - return nil + if child.label?.starts(with: "_") == true, + let element = child.value as? any AnyConfigurationElement { + return element.description.options } - guard let element = child.value as? any AnyConfigurationElement else { - return nil - } - return element.description - }.flatMap(\.options) + return [] + } guard options.isNotEmpty else { queuedFatalError( """ From 010cbd8bc518ad52b70ee77639fb70f491eae450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 18 Dec 2024 16:12:52 +0100 Subject: [PATCH 112/260] Reduce target-specific imports (#5898) --- Source/SwiftLintCore/Helpers/Glob.swift | 16 +++++----------- Source/swiftlint/Commands/Rules.swift | 7 ------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Source/SwiftLintCore/Helpers/Glob.swift b/Source/SwiftLintCore/Helpers/Glob.swift index d025da520e..8285366d64 100644 --- a/Source/SwiftLintCore/Helpers/Glob.swift +++ b/Source/SwiftLintCore/Helpers/Glob.swift @@ -1,15 +1,9 @@ import Foundation -#if canImport(Darwin) -import Darwin - -private let globFunction = Darwin.glob -#elseif canImport(Glibc) -import Glibc - -private let globFunction = Glibc.glob -#else -#error("Unsupported platform") +#if os(Linux) +#if canImport(Glibc) +import func Glibc.glob +#endif #endif // Adapted from https://gist.github.com/efirestone/ce01ae109e08772647eb061b3bb387c3 @@ -26,7 +20,7 @@ struct Glob { var globResult = glob_t() defer { globfree(&globResult) } - if globFunction(pattern, GLOB_TILDE | GLOB_BRACE | GLOB_MARK, nil, &globResult) == 0 { + if glob(pattern, GLOB_TILDE | GLOB_BRACE | GLOB_MARK, nil, &globResult) == 0 { paths.append(contentsOf: populateFiles(globResult: globResult)) } } diff --git a/Source/swiftlint/Commands/Rules.swift b/Source/swiftlint/Commands/Rules.swift index 0ed3a966a7..c76f9bd5b3 100644 --- a/Source/swiftlint/Commands/Rules.swift +++ b/Source/swiftlint/Commands/Rules.swift @@ -1,11 +1,4 @@ import ArgumentParser -#if canImport(Darwin) -import Darwin -#elseif canImport(Glibc) -import Glibc -#else -#error("Unsupported platform") -#endif import Foundation import SwiftLintFramework import SwiftyTextTable From ebdd23a4adf918dad5954b98918136915b74aa13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 18 Dec 2024 19:53:34 +0100 Subject: [PATCH 113/260] Support Musl C library (#5899) --- Source/SwiftLintCore/Helpers/Glob.swift | 9 ++++++++- Source/SwiftLintFramework/ExitHelper.swift | 8 ++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Source/SwiftLintCore/Helpers/Glob.swift b/Source/SwiftLintCore/Helpers/Glob.swift index 8285366d64..4ef3cfbacf 100644 --- a/Source/SwiftLintCore/Helpers/Glob.swift +++ b/Source/SwiftLintCore/Helpers/Glob.swift @@ -3,6 +3,8 @@ import Foundation #if os(Linux) #if canImport(Glibc) import func Glibc.glob +#elseif canImport(Musl) +import func Musl.glob #endif #endif @@ -20,7 +22,12 @@ struct Glob { var globResult = glob_t() defer { globfree(&globResult) } - if glob(pattern, GLOB_TILDE | GLOB_BRACE | GLOB_MARK, nil, &globResult) == 0 { + #if canImport(Musl) + let flags = GLOB_TILDE | GLOB_MARK + #else + let flags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK + #endif + if glob(pattern, flags, nil, &globResult) == 0 { paths.append(contentsOf: populateFiles(globResult: globResult)) } } diff --git a/Source/SwiftLintFramework/ExitHelper.swift b/Source/SwiftLintFramework/ExitHelper.swift index f2bf1281ea..1f363ddaf9 100644 --- a/Source/SwiftLintFramework/ExitHelper.swift +++ b/Source/SwiftLintFramework/ExitHelper.swift @@ -1,12 +1,16 @@ #if os(Linux) -import Glibc +#if canImport(Glibc) +import func Glibc.exit +#elseif canImport(Musl) +import func Musl.exit +#endif #endif package enum ExitHelper { package static func successfullyExit() { #if os(Linux) // Workaround for https://github.com/apple/swift/issues/59961 - Glibc.exit(0) + exit(0) #endif } } From 2cf298bf15ec5a6415c675aae9241da03f78d6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 19 Dec 2024 18:57:18 +0100 Subject: [PATCH 114/260] Remove exit helper workaround (#5900) Introduced in 4fb5ebe19b1121b84915cb213f7bd027779fe2aa due to https://github.com/swiftlang/swift/issues/59961. --- Source/SwiftLintFramework/ExitHelper.swift | 16 ---------------- .../LintOrAnalyzeCommand.swift | 1 - Source/swiftlint/Commands/Baseline.swift | 2 -- Source/swiftlint/Commands/Docs.swift | 1 - Source/swiftlint/Commands/GenerateDocs.swift | 1 - Source/swiftlint/Commands/Reporters.swift | 1 - Source/swiftlint/Commands/Rules.swift | 2 -- Source/swiftlint/Commands/Version.swift | 1 - 8 files changed, 25 deletions(-) delete mode 100644 Source/SwiftLintFramework/ExitHelper.swift diff --git a/Source/SwiftLintFramework/ExitHelper.swift b/Source/SwiftLintFramework/ExitHelper.swift deleted file mode 100644 index 1f363ddaf9..0000000000 --- a/Source/SwiftLintFramework/ExitHelper.swift +++ /dev/null @@ -1,16 +0,0 @@ -#if os(Linux) -#if canImport(Glibc) -import func Glibc.exit -#elseif canImport(Musl) -import func Musl.exit -#endif -#endif - -package enum ExitHelper { - package static func successfullyExit() { -#if os(Linux) - // Workaround for https://github.com/apple/swift/issues/59961 - exit(0) -#endif - } -} diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 746342c301..6aff23f998 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -137,7 +137,6 @@ package struct LintOrAnalyzeCommand { try await Signposts.record(name: "LintOrAnalyzeCommand.run") { try await options.autocorrect ? autocorrect(options) : lintOrAnalyze(options) } - ExitHelper.successfullyExit() } private static func lintOrAnalyze(_ options: LintOrAnalyzeOptions) async throws { diff --git a/Source/swiftlint/Commands/Baseline.swift b/Source/swiftlint/Commands/Baseline.swift index a58b889c87..fa0070ba2d 100644 --- a/Source/swiftlint/Commands/Baseline.swift +++ b/Source/swiftlint/Commands/Baseline.swift @@ -39,7 +39,6 @@ extension SwiftLint { func run() throws { let savedBaseline = try SwiftLintCore.Baseline(fromPath: options.baseline) try report(savedBaseline.violations, using: reportingOptions.reporter, to: reportingOptions.output) - ExitHelper.successfullyExit() } } @@ -65,7 +64,6 @@ extension SwiftLint { let baseline = try SwiftLintCore.Baseline(fromPath: options.baseline) let otherBaseline = try SwiftLintCore.Baseline(fromPath: otherBaseline) try report(baseline.compare(otherBaseline), using: reportingOptions.reporter, to: reportingOptions.output) - ExitHelper.successfullyExit() } } } diff --git a/Source/swiftlint/Commands/Docs.swift b/Source/swiftlint/Commands/Docs.swift index a3282de651..5b27552b30 100644 --- a/Source/swiftlint/Commands/Docs.swift +++ b/Source/swiftlint/Commands/Docs.swift @@ -22,7 +22,6 @@ extension SwiftLint { } } open(URL(string: "https://realm.github.io/SwiftLint/\(subPage)")!) - ExitHelper.successfullyExit() } } } diff --git a/Source/swiftlint/Commands/GenerateDocs.swift b/Source/swiftlint/Commands/GenerateDocs.swift index b62e6ae8f9..0a8264245c 100644 --- a/Source/swiftlint/Commands/GenerateDocs.swift +++ b/Source/swiftlint/Commands/GenerateDocs.swift @@ -22,7 +22,6 @@ extension SwiftLint { try RuleListDocumentation(rules) .write(to: URL(fileURLWithPath: path, isDirectory: true)) - ExitHelper.successfullyExit() } } } diff --git a/Source/swiftlint/Commands/Reporters.swift b/Source/swiftlint/Commands/Reporters.swift index 345a93a0a7..56be3b3174 100644 --- a/Source/swiftlint/Commands/Reporters.swift +++ b/Source/swiftlint/Commands/Reporters.swift @@ -8,7 +8,6 @@ extension SwiftLint { func run() throws { print(TextTable(reporters: reportersList).render()) - ExitHelper.successfullyExit() } } } diff --git a/Source/swiftlint/Commands/Rules.swift b/Source/swiftlint/Commands/Rules.swift index c76f9bd5b3..ea79fc957a 100644 --- a/Source/swiftlint/Commands/Rules.swift +++ b/Source/swiftlint/Commands/Rules.swift @@ -29,7 +29,6 @@ extension SwiftLint { throw SwiftLintError.usageError(description: "No rule with identifier: \(ruleID)") } printDescription(for: rule, with: configuration) - ExitHelper.successfullyExit() return } let rules = RulesFilter(enabledRules: configuration.rules) @@ -50,7 +49,6 @@ extension SwiftLint { ) print(table.render()) } - ExitHelper.successfullyExit() } private func printDescription(for ruleType: any Rule.Type, with configuration: Configuration) { diff --git a/Source/swiftlint/Commands/Version.swift b/Source/swiftlint/Commands/Version.swift index 0aa32567d1..539153b568 100644 --- a/Source/swiftlint/Commands/Version.swift +++ b/Source/swiftlint/Commands/Version.swift @@ -22,7 +22,6 @@ extension SwiftLint { if checkForUpdates { await UpdateChecker.checkForUpdates() } - ExitHelper.successfullyExit() } } } From bd73547fa47b74e4d3b0551e223ae742f6c8f0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 19 Dec 2024 20:46:32 +0100 Subject: [PATCH 115/260] Remove deleted disabled rules (#5901) --- .swiftlint.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 2529e986ae..c5f771a23b 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -28,7 +28,6 @@ disabled_rules: - force_unwrapping - function_default_parameter_at_end - indentation_width - - inert_defer - missing_docs - multiline_arguments - multiline_arguments_brackets @@ -48,7 +47,6 @@ disabled_rules: - todo - trailing_closure - type_contents_order - - unused_capture_list - vertical_whitespace_between_cases # Configurations From 5ab8f09161fc0460099a061ef8db1bf1be5b0452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 21 Dec 2024 19:41:31 +0100 Subject: [PATCH 116/260] Check for explicitly set Docker tag --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7736846b9e..fd25557deb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Docker tag - if: github.event_name != 'push' && ${{ inputs.tag }} + if: github.event_name != 'push' && ${{ inputs.tag != 'latest' }} run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - name: Use default Docker tag if: github.event_name == 'push' From 88787fd9bb68a08a2725d9f05eccda1e4c9c9eda Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Mon, 23 Dec 2024 11:11:03 +0000 Subject: [PATCH 117/260] Still check for updates if we have violations (#5905) --- CHANGELOG.md | 9 +++++++-- Source/SwiftLintFramework/LintOrAnalyzeCommand.swift | 12 +++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a67a377089..afa561b330 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,12 +28,12 @@ * Support replacing identity expressions with `\.self` in `prefer_key_path` rule from Swift 6 on. [SimplyDanny](https://github.com/SimplyDanny) - + * Support linting only provided file paths with command plugins. [DanSkeel](https://github.com/DanSkeel) * Add new category for `@IBSegueAction` to `type_contents_order` rule. - [dk-talks](https://github.com/dk-talks) + [dk-talks](https://github.com/dk-talks) [SimplyDanny](https://github.com/SimplyDanny) #### Bug Fixes @@ -42,6 +42,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5886](ttps://github.com/realm/SwiftLint/issues/5886) +* If violations are detected by `lint` or `analyze`, still perform an update + check for new versions of SwiftLint if requested. + [Martin Redington](https://github.com/mildm8nnered) + [#5904](ttps://github.com/realm/SwiftLint/issues/5904) + ## 0.57.1: Squeaky Clean Cycle #### Breaking diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 6aff23f998..3f2bcdfbcc 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -145,12 +145,15 @@ package struct LintOrAnalyzeCommand { if let baselineOutputPath = options.writeBaseline ?? builder.configuration.writeBaseline { try Baseline(violations: builder.unfilteredViolations).write(toPath: baselineOutputPath) } - try Signposts.record(name: "LintOrAnalyzeCommand.PostProcessViolations") { + let numberOfSeriousViolations = try Signposts.record(name: "LintOrAnalyzeCommand.PostProcessViolations") { try postProcessViolations(files: files, builder: builder) } if options.checkForUpdates || builder.configuration.checkForUpdates { await UpdateChecker.checkForUpdates() } + if numberOfSeriousViolations > 0 { + exit(2) + } } private static func collectViolations(builder: LintOrAnalyzeResultBuilder) async throws -> [SwiftLintFile] { @@ -194,7 +197,10 @@ package struct LintOrAnalyzeCommand { } } - private static func postProcessViolations(files: [SwiftLintFile], builder: LintOrAnalyzeResultBuilder) throws { + private static func postProcessViolations( + files: [SwiftLintFile], + builder: LintOrAnalyzeResultBuilder + ) throws -> Int { let options = builder.options let configuration = builder.configuration if isWarningThresholdBroken(configuration: configuration, violations: builder.violations) @@ -221,7 +227,7 @@ package struct LintOrAnalyzeCommand { } } try builder.cache?.save() - guard numberOfSeriousViolations == 0 else { exit(2) } + return numberOfSeriousViolations } private static func baseline(_ options: LintOrAnalyzeOptions, _ configuration: Configuration) throws -> Baseline? { From a6c4fd98bcec4538c8d9ec74b83cba734e6019ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 4 Mar 2024 21:41:20 +0100 Subject: [PATCH 118/260] Move files from SwiftLintCore to SwiftLintFramework Ideally, SwiftLintCore would some day only contain components that are needed to define rules. Consequently, it would be the only bundle required to import for (external) rule development. --- Makefile | 10 +++++----- .../NSRegularExpression+SwiftLint.swift | 2 +- .../Extensions/SwiftLintFile+Cache.swift | 4 ++-- Source/SwiftLintCore/Models/Command.swift | 4 ++-- Source/SwiftLintCore/Models/Example.swift | 2 +- Source/SwiftLintCore/Models/Issue.swift | 18 +++++++----------- Source/SwiftLintCore/Models/RuleList.swift | 6 +++--- .../SwiftLintCore/Models/StyleViolation.swift | 9 ++++++++- .../RegexConfiguration.swift | 4 ++-- .../Configuration}/Configuration+Cache.swift | 0 .../Configuration+FileGraph.swift | 0 .../Configuration+FileGraphSubtypes.swift | 0 .../Configuration+IndentationStyle.swift | 2 +- .../Configuration+LintableFiles.swift | 5 +++-- .../Configuration}/Configuration+Merging.swift | 0 .../Configuration}/Configuration+Parsing.swift | 0 .../Configuration}/Configuration+Remote.swift | 0 .../Configuration+RulesMode.swift | 0 .../Configuration+RulesWrapper.swift | 0 .../Configuration}/Configuration.swift | 0 .../Documentation/RuleDocumentation.swift | 0 .../Documentation/RuleListDocumentation.swift | 0 .../Extensions/FileManager+SwiftLint.swift | 0 .../Extensions/String+XML.swift | 0 .../Extensions/String+sha256.swift | 0 .../Helpers/ExecutableInfo.swift | 0 .../Helpers/Glob.swift | 0 .../Helpers/Reachability.swift | 0 .../Models/CustomRuleTimer.swift | 0 ...shableConfigurationRuleWrapperWrapper.swift | 0 .../Models/Linter.swift | 0 .../Models/LinterCache.swift | 0 .../Models/ReportersList.swift | 0 .../Models/Version.swift | 0 .../Models/YamlParser.swift | 0 .../Reporters/CSVReporter.swift | 0 .../Reporters/CheckstyleReporter.swift | 0 .../Reporters/CodeClimateReporter.swift | 0 .../Reporters/EmojiReporter.swift | 0 .../GitHubActionsLoggingReporter.swift | 0 .../Reporters/GitLabJUnitReporter.swift | 0 .../Reporters/HTMLReporter.swift | 0 .../Reporters/JSONReporter.swift | 0 .../Reporters/JUnitReporter.swift | 0 .../Reporters/MarkdownReporter.swift | 0 .../Reporters/RelativePathReporter.swift | 0 .../Reporters}/Reporter.swift | 0 .../Reporters/SARIFReporter.swift | 0 .../Reporters/SonarQubeReporter.swift | 0 .../Reporters/SummaryReporter.swift | 0 .../Reporters/XcodeReporter.swift | 9 +-------- .../Rules/CoreRules.swift | 0 .../Rules/CustomRules.swift | 0 .../Rules/SuperfluousDisableCommandRule.swift | 0 .../CollectingRuleTests.swift | 2 +- .../SwiftLintFrameworkTests/CommandTests.swift | 2 +- .../ConfigurationAliasesTests.swift | 2 +- .../ConfigurationTests+Mock.swift | 2 +- .../ConfigurationTests+MultipleConfigs.swift | 2 +- .../ConfigurationTests.swift | 2 +- .../CustomRulesTests.swift | 3 ++- .../ExpiringTodoRuleTests.swift | 1 + Tests/SwiftLintFrameworkTests/GlobTests.swift | 2 +- .../LinterCacheTests.swift | 2 +- .../ReporterTests.swift | 6 +++--- .../SourceKitCrashTests.swift | 2 +- .../YamlParserTests.swift | 2 +- Tests/SwiftLintTestHelpers/TestHelpers.swift | 2 +- tools/get-version | 2 +- 69 files changed, 54 insertions(+), 55 deletions(-) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+Cache.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+FileGraph.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+FileGraphSubtypes.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+IndentationStyle.swift (94%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+LintableFiles.swift (96%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+Merging.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+Parsing.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+Remote.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+RulesMode.swift (100%) rename Source/{SwiftLintCore/Extensions => SwiftLintFramework/Configuration}/Configuration+RulesWrapper.swift (100%) rename Source/{SwiftLintCore/Models => SwiftLintFramework/Configuration}/Configuration.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Documentation/RuleDocumentation.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Documentation/RuleListDocumentation.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Extensions/FileManager+SwiftLint.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Extensions/String+XML.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Extensions/String+sha256.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Helpers/ExecutableInfo.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Helpers/Glob.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Helpers/Reachability.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/CustomRuleTimer.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/HashableConfigurationRuleWrapperWrapper.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/Linter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/LinterCache.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/ReportersList.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/Version.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Models/YamlParser.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/CSVReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/CheckstyleReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/CodeClimateReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/EmojiReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/GitHubActionsLoggingReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/GitLabJUnitReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/HTMLReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/JSONReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/JUnitReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/MarkdownReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/RelativePathReporter.swift (100%) rename Source/{SwiftLintCore/Protocols => SwiftLintFramework/Reporters}/Reporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/SARIFReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/SonarQubeReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/SummaryReporter.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Reporters/XcodeReporter.swift (70%) rename Source/{SwiftLintCore => SwiftLintFramework}/Rules/CoreRules.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Rules/CustomRules.swift (100%) rename Source/{SwiftLintCore => SwiftLintFramework}/Rules/SuperfluousDisableCommandRule.swift (100%) diff --git a/Makefile b/Makefile index b624d78cac..c65cf32b69 100644 --- a/Makefile +++ b/Makefile @@ -32,15 +32,15 @@ VERSION_STRING=$(shell ./tools/get-version) all: build -sourcery: Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift Source/SwiftLintCore/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift +sourcery: Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift Source/SwiftLintFramework/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift: Source/SwiftLintBuiltInRules/Rules/**/*.swift .sourcery/BuiltInRules.stencil ./tools/sourcery --sources Source/SwiftLintBuiltInRules/Rules --templates .sourcery/BuiltInRules.stencil --output .sourcery mv .sourcery/BuiltInRules.generated.swift Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift -Source/SwiftLintCore/Models/ReportersList.swift: Source/SwiftLintCore/Reporters/*.swift .sourcery/ReportersList.stencil - ./tools/sourcery --sources Source/SwiftLintCore/Reporters --templates .sourcery/ReportersList.stencil --output .sourcery - mv .sourcery/ReportersList.generated.swift Source/SwiftLintCore/Models/ReportersList.swift +Source/SwiftLintFramework/Models/ReportersList.swift: Source/SwiftLintFramework/Reporters/*.swift .sourcery/ReportersList.stencil + ./tools/sourcery --sources Source/SwiftLintFramework/Reporters --templates .sourcery/ReportersList.stencil --output .sourcery + mv .sourcery/ReportersList.generated.swift Source/SwiftLintFramework/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift: Source/SwiftLint*/Rules/**/*.swift .sourcery/GeneratedTests.stencil ./tools/sourcery --sources Source/SwiftLintBuiltInRules/Rules --templates .sourcery/GeneratedTests.stencil --output .sourcery @@ -187,7 +187,7 @@ endif $(eval NEW_VERSION_AND_NAME := $(filter-out $@,$(MAKECMDGOALS))) $(eval NEW_VERSION := $(shell echo $(NEW_VERSION_AND_NAME) | sed 's/:.*//' )) @sed -i '' 's/## Main/## $(NEW_VERSION_AND_NAME)/g' CHANGELOG.md - @sed 's/__VERSION__/$(NEW_VERSION)/g' tools/Version.swift.template > Source/SwiftLintCore/Models/Version.swift + @sed 's/__VERSION__/$(NEW_VERSION)/g' tools/Version.swift.template > Source/SwiftLintFramework/Models/Version.swift @sed -e '3s/.*/ version = "$(NEW_VERSION)",/' -i '' MODULE.bazel make clean make package diff --git a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift index 4f392828ea..417f4da6f2 100644 --- a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift @@ -15,7 +15,7 @@ public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLitera try! self.init(pattern: value) } - var pattern: String { regex.pattern } + package var pattern: String { regex.pattern } var numberOfCaptureGroups: Int { regex.numberOfCaptureGroups } diff --git a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift index 65a9bf58bd..8f69627099 100644 --- a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift +++ b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift @@ -50,7 +50,7 @@ private let syntaxKindsByLinesCache = Cache { $0.syntaxKindsByLine() } private let syntaxTokensByLinesCache = Cache { $0.syntaxTokensByLine() } private let linesWithTokensCache = Cache { $0.computeLinesWithTokens() } -internal typealias AssertHandler = () -> Void +package typealias AssertHandler = () -> Void // Re-enable once all parser diagnostics in tests have been addressed. // https://github.com/realm/SwiftLint/issues/3348 package var parserDiagnosticsDisabledForTests = false @@ -114,7 +114,7 @@ extension SwiftLintFile { } } - internal var assertHandler: AssertHandler? { + package var assertHandler: AssertHandler? { get { assertHandlerCache.get(self) } diff --git a/Source/SwiftLintCore/Models/Command.swift b/Source/SwiftLintCore/Models/Command.swift index 375cdf00a6..9ebea5b52d 100644 --- a/Source/SwiftLintCore/Models/Command.swift +++ b/Source/SwiftLintCore/Models/Command.swift @@ -13,7 +13,7 @@ public struct Command: Equatable { /// - returns: The inverse action that can cancel out the current action, restoring the SwifttLint engine's /// state prior to the current action. - internal func inverse() -> Self { + package func inverse() -> Self { switch self { case .enable: return .disable case .disable: return .enable @@ -147,7 +147,7 @@ public struct Command: Equatable { /// If the command doesn't have a modifier, it is returned as-is. /// /// - returns: The expanded commands. - internal func expand() -> [Self] { + package func expand() -> [Self] { guard let modifier else { return [self] } diff --git a/Source/SwiftLintCore/Models/Example.swift b/Source/SwiftLintCore/Models/Example.swift index 1c49edbb15..a91d023198 100644 --- a/Source/SwiftLintCore/Models/Example.swift +++ b/Source/SwiftLintCore/Models/Example.swift @@ -37,7 +37,7 @@ public struct Example: Sendable { /// why a rule is applied and where not. Complex examples with rarely used language constructs or /// pathological use cases which are indeed important to test but not helpful for understanding can be /// hidden from the documentation with this option. - let excludeFromDocumentation: Bool + package let excludeFromDocumentation: Bool /// Specifies whether the test example should be the only example run during the current test case execution. package var isFocused: Bool diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index 2a35b36863..d09affb2d3 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -107,17 +107,17 @@ public enum Issue: LocalizedError, Equatable { /// - parameter error: Any `Error`. /// /// - returns: A `SwiftLintError.genericWarning` containing the message of the `error` argument. - static func wrap(error: some Error) -> Self { + package static func wrap(error: some Error) -> Self { error as? Self ?? Self.genericWarning(error.localizedDescription) } /// Make this issue an error. - var asError: Self { + package var asError: Self { Self.genericError(message) } /// The issues description which is ready to be printed to the console. - var errorDescription: String { + package var errorDescription: String { switch self { case .genericError: return "error: \(message)" @@ -164,19 +164,15 @@ public enum Issue: LocalizedError, Equatable { case let .invalidRuleIDs(ruleIDs): return "The key(s) \(ruleIDs.formatted) used as rule identifier(s) is/are invalid." case let .ruleNotPresentInOnlyRules(id): - return "Found a configuration for '\(id)' rule, but it is not present in " + - "'\(Configuration.Key.onlyRules.rawValue)'." + return "Found a configuration for '\(id)' rule, but it is not present in 'only_rules'." case let .ruleDisabledInDisabledRules(id): - return "Found a configuration for '\(id)' rule, but it is disabled in " + - "'\(Configuration.Key.disabledRules.rawValue)'." + return "Found a configuration for '\(id)' rule, but it is disabled in 'disabled_rules'." case let .ruleDisabledInParentConfiguration(id): return "Found a configuration for '\(id)' rule, but it is disabled in a parent configuration." case let .ruleNotEnabledInOptInRules(id): - return "Found a configuration for '\(id)' rule, but it is not enabled in " + - "'\(Configuration.Key.optInRules.rawValue)'." + return "Found a configuration for '\(id)' rule, but it is not enabled in 'opt_in_rules'." case let .ruleNotEnabledInParentOnlyRules(id): - return "Found a configuration for '\(id)' rule, but it is not present in the parent's " + - "'\(Configuration.Key.onlyRules.rawValue)'." + return "Found a configuration for '\(id)' rule, but it is not present in the parent's 'only_rules'." case let .genericWarning(message), let .genericError(message): return message case let .ruleDeprecated(id): diff --git a/Source/SwiftLintCore/Models/RuleList.swift b/Source/SwiftLintCore/Models/RuleList.swift index 79dab75ac6..74978e997f 100644 --- a/Source/SwiftLintCore/Models/RuleList.swift +++ b/Source/SwiftLintCore/Models/RuleList.swift @@ -40,7 +40,7 @@ public struct RuleList { // MARK: - Internal - internal func allRulesWrapped(configurationDict: [String: Any] = [:]) throws -> [ConfigurationRuleWrapper] { + package func allRulesWrapped(configurationDict: [String: Any] = [:]) throws -> [ConfigurationRuleWrapper] { var rules = [String: ConfigurationRuleWrapper]() // Add rules where configuration exists @@ -72,11 +72,11 @@ public struct RuleList { return Array(rules.values) } - internal func identifier(for alias: String) -> String? { + package func identifier(for alias: String) -> String? { aliases[alias] } - internal func allValidIdentifiers() -> [String] { + package func allValidIdentifiers() -> [String] { list.flatMap { _, rule -> [String] in rule.description.allIdentifiers } diff --git a/Source/SwiftLintCore/Models/StyleViolation.swift b/Source/SwiftLintCore/Models/StyleViolation.swift index 96a0aecabc..3defc96fe6 100644 --- a/Source/SwiftLintCore/Models/StyleViolation.swift +++ b/Source/SwiftLintCore/Models/StyleViolation.swift @@ -20,7 +20,14 @@ public struct StyleViolation: CustomStringConvertible, Codable, Hashable { /// A printable description for this violation. public var description: String { - XcodeReporter.generateForSingleViolation(self) + // {full_path_to_file}{:line}{:character}: {error,warning}: {content} + [ + "\(location): ", + "\(severity.rawValue): ", + "\(ruleName) Violation: ", + reason, + " (\(ruleIdentifier))", + ].joined() } /// Creates a `StyleViolation` by specifying its properties directly. diff --git a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift index c4d3acf106..09ab2d12a1 100644 --- a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift +++ b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift @@ -12,7 +12,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, public var message = "Regex matched" /// The regular expression to apply to trigger violations for this custom rule. @ConfigurationElement(key: "regex") - var regex: RegularExpression! // swiftlint:disable:this implicitly_unwrapped_optional + package var regex: RegularExpression! // swiftlint:disable:this implicitly_unwrapped_optional /// Regular expressions to include when matching the file path. public var included: [NSRegularExpression] = [] /// Regular expressions to exclude when matching the file path. @@ -104,7 +104,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, hasher.combine(identifier) } - func shouldValidate(filePath: String) -> Bool { + package func shouldValidate(filePath: String) -> Bool { let pathRange = filePath.fullNSRange let isIncluded = included.isEmpty || included.contains { regex in regex.firstMatch(in: filePath, range: pathRange) != nil diff --git a/Source/SwiftLintCore/Extensions/Configuration+Cache.swift b/Source/SwiftLintFramework/Configuration/Configuration+Cache.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+Cache.swift rename to Source/SwiftLintFramework/Configuration/Configuration+Cache.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift b/Source/SwiftLintFramework/Configuration/Configuration+FileGraph.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+FileGraph.swift rename to Source/SwiftLintFramework/Configuration/Configuration+FileGraph.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+FileGraphSubtypes.swift b/Source/SwiftLintFramework/Configuration/Configuration+FileGraphSubtypes.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+FileGraphSubtypes.swift rename to Source/SwiftLintFramework/Configuration/Configuration+FileGraphSubtypes.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+IndentationStyle.swift b/Source/SwiftLintFramework/Configuration/Configuration+IndentationStyle.swift similarity index 94% rename from Source/SwiftLintCore/Extensions/Configuration+IndentationStyle.swift rename to Source/SwiftLintFramework/Configuration/Configuration+IndentationStyle.swift index 2db8ba5460..e66fce9aff 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+IndentationStyle.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+IndentationStyle.swift @@ -1,6 +1,6 @@ public extension Configuration { /// The style of indentation used in a Swift project. - enum IndentationStyle: Hashable { + enum IndentationStyle: Hashable, Sendable { /// Swift source code should be indented using tabs. case tabs /// Swift source code should be indented using spaces with `count` spaces per indentation level. diff --git a/Source/SwiftLintCore/Extensions/Configuration+LintableFiles.swift b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift similarity index 96% rename from Source/SwiftLintCore/Extensions/Configuration+LintableFiles.swift rename to Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift index 79617bf622..30ea7be975 100644 --- a/Source/SwiftLintCore/Extensions/Configuration+LintableFiles.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift @@ -96,8 +96,9 @@ extension Configuration { /// - returns: The input paths after removing the excluded paths. public func filterExcludedPathsByPrefix(in paths: [String]...) -> [String] { let allPaths = paths.flatMap { $0 } - let excludedPaths = self.excludedPaths.parallelFlatMap(transform: Glob.resolveGlob) - .map { $0.absolutePathStandardized() } + let excludedPaths = self.excludedPaths + .parallelFlatMap { @Sendable in Glob.resolveGlob($0) } + .map { $0.absolutePathStandardized() } return allPaths.filter { path in !excludedPaths.contains { path.hasPrefix($0) } } diff --git a/Source/SwiftLintCore/Extensions/Configuration+Merging.swift b/Source/SwiftLintFramework/Configuration/Configuration+Merging.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+Merging.swift rename to Source/SwiftLintFramework/Configuration/Configuration+Merging.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+Parsing.swift b/Source/SwiftLintFramework/Configuration/Configuration+Parsing.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+Parsing.swift rename to Source/SwiftLintFramework/Configuration/Configuration+Parsing.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+Remote.swift b/Source/SwiftLintFramework/Configuration/Configuration+Remote.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+Remote.swift rename to Source/SwiftLintFramework/Configuration/Configuration+Remote.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift b/Source/SwiftLintFramework/Configuration/Configuration+RulesMode.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+RulesMode.swift rename to Source/SwiftLintFramework/Configuration/Configuration+RulesMode.swift diff --git a/Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift b/Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/Configuration+RulesWrapper.swift rename to Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift diff --git a/Source/SwiftLintCore/Models/Configuration.swift b/Source/SwiftLintFramework/Configuration/Configuration.swift similarity index 100% rename from Source/SwiftLintCore/Models/Configuration.swift rename to Source/SwiftLintFramework/Configuration/Configuration.swift diff --git a/Source/SwiftLintCore/Documentation/RuleDocumentation.swift b/Source/SwiftLintFramework/Documentation/RuleDocumentation.swift similarity index 100% rename from Source/SwiftLintCore/Documentation/RuleDocumentation.swift rename to Source/SwiftLintFramework/Documentation/RuleDocumentation.swift diff --git a/Source/SwiftLintCore/Documentation/RuleListDocumentation.swift b/Source/SwiftLintFramework/Documentation/RuleListDocumentation.swift similarity index 100% rename from Source/SwiftLintCore/Documentation/RuleListDocumentation.swift rename to Source/SwiftLintFramework/Documentation/RuleListDocumentation.swift diff --git a/Source/SwiftLintCore/Extensions/FileManager+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/FileManager+SwiftLint.swift rename to Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift diff --git a/Source/SwiftLintCore/Extensions/String+XML.swift b/Source/SwiftLintFramework/Extensions/String+XML.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/String+XML.swift rename to Source/SwiftLintFramework/Extensions/String+XML.swift diff --git a/Source/SwiftLintCore/Extensions/String+sha256.swift b/Source/SwiftLintFramework/Extensions/String+sha256.swift similarity index 100% rename from Source/SwiftLintCore/Extensions/String+sha256.swift rename to Source/SwiftLintFramework/Extensions/String+sha256.swift diff --git a/Source/SwiftLintCore/Helpers/ExecutableInfo.swift b/Source/SwiftLintFramework/Helpers/ExecutableInfo.swift similarity index 100% rename from Source/SwiftLintCore/Helpers/ExecutableInfo.swift rename to Source/SwiftLintFramework/Helpers/ExecutableInfo.swift diff --git a/Source/SwiftLintCore/Helpers/Glob.swift b/Source/SwiftLintFramework/Helpers/Glob.swift similarity index 100% rename from Source/SwiftLintCore/Helpers/Glob.swift rename to Source/SwiftLintFramework/Helpers/Glob.swift diff --git a/Source/SwiftLintCore/Helpers/Reachability.swift b/Source/SwiftLintFramework/Helpers/Reachability.swift similarity index 100% rename from Source/SwiftLintCore/Helpers/Reachability.swift rename to Source/SwiftLintFramework/Helpers/Reachability.swift diff --git a/Source/SwiftLintCore/Models/CustomRuleTimer.swift b/Source/SwiftLintFramework/Models/CustomRuleTimer.swift similarity index 100% rename from Source/SwiftLintCore/Models/CustomRuleTimer.swift rename to Source/SwiftLintFramework/Models/CustomRuleTimer.swift diff --git a/Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift b/Source/SwiftLintFramework/Models/HashableConfigurationRuleWrapperWrapper.swift similarity index 100% rename from Source/SwiftLintCore/Models/HashableConfigurationRuleWrapperWrapper.swift rename to Source/SwiftLintFramework/Models/HashableConfigurationRuleWrapperWrapper.swift diff --git a/Source/SwiftLintCore/Models/Linter.swift b/Source/SwiftLintFramework/Models/Linter.swift similarity index 100% rename from Source/SwiftLintCore/Models/Linter.swift rename to Source/SwiftLintFramework/Models/Linter.swift diff --git a/Source/SwiftLintCore/Models/LinterCache.swift b/Source/SwiftLintFramework/Models/LinterCache.swift similarity index 100% rename from Source/SwiftLintCore/Models/LinterCache.swift rename to Source/SwiftLintFramework/Models/LinterCache.swift diff --git a/Source/SwiftLintCore/Models/ReportersList.swift b/Source/SwiftLintFramework/Models/ReportersList.swift similarity index 100% rename from Source/SwiftLintCore/Models/ReportersList.swift rename to Source/SwiftLintFramework/Models/ReportersList.swift diff --git a/Source/SwiftLintCore/Models/Version.swift b/Source/SwiftLintFramework/Models/Version.swift similarity index 100% rename from Source/SwiftLintCore/Models/Version.swift rename to Source/SwiftLintFramework/Models/Version.swift diff --git a/Source/SwiftLintCore/Models/YamlParser.swift b/Source/SwiftLintFramework/Models/YamlParser.swift similarity index 100% rename from Source/SwiftLintCore/Models/YamlParser.swift rename to Source/SwiftLintFramework/Models/YamlParser.swift diff --git a/Source/SwiftLintCore/Reporters/CSVReporter.swift b/Source/SwiftLintFramework/Reporters/CSVReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/CSVReporter.swift rename to Source/SwiftLintFramework/Reporters/CSVReporter.swift diff --git a/Source/SwiftLintCore/Reporters/CheckstyleReporter.swift b/Source/SwiftLintFramework/Reporters/CheckstyleReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/CheckstyleReporter.swift rename to Source/SwiftLintFramework/Reporters/CheckstyleReporter.swift diff --git a/Source/SwiftLintCore/Reporters/CodeClimateReporter.swift b/Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/CodeClimateReporter.swift rename to Source/SwiftLintFramework/Reporters/CodeClimateReporter.swift diff --git a/Source/SwiftLintCore/Reporters/EmojiReporter.swift b/Source/SwiftLintFramework/Reporters/EmojiReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/EmojiReporter.swift rename to Source/SwiftLintFramework/Reporters/EmojiReporter.swift diff --git a/Source/SwiftLintCore/Reporters/GitHubActionsLoggingReporter.swift b/Source/SwiftLintFramework/Reporters/GitHubActionsLoggingReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/GitHubActionsLoggingReporter.swift rename to Source/SwiftLintFramework/Reporters/GitHubActionsLoggingReporter.swift diff --git a/Source/SwiftLintCore/Reporters/GitLabJUnitReporter.swift b/Source/SwiftLintFramework/Reporters/GitLabJUnitReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/GitLabJUnitReporter.swift rename to Source/SwiftLintFramework/Reporters/GitLabJUnitReporter.swift diff --git a/Source/SwiftLintCore/Reporters/HTMLReporter.swift b/Source/SwiftLintFramework/Reporters/HTMLReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/HTMLReporter.swift rename to Source/SwiftLintFramework/Reporters/HTMLReporter.swift diff --git a/Source/SwiftLintCore/Reporters/JSONReporter.swift b/Source/SwiftLintFramework/Reporters/JSONReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/JSONReporter.swift rename to Source/SwiftLintFramework/Reporters/JSONReporter.swift diff --git a/Source/SwiftLintCore/Reporters/JUnitReporter.swift b/Source/SwiftLintFramework/Reporters/JUnitReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/JUnitReporter.swift rename to Source/SwiftLintFramework/Reporters/JUnitReporter.swift diff --git a/Source/SwiftLintCore/Reporters/MarkdownReporter.swift b/Source/SwiftLintFramework/Reporters/MarkdownReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/MarkdownReporter.swift rename to Source/SwiftLintFramework/Reporters/MarkdownReporter.swift diff --git a/Source/SwiftLintCore/Reporters/RelativePathReporter.swift b/Source/SwiftLintFramework/Reporters/RelativePathReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/RelativePathReporter.swift rename to Source/SwiftLintFramework/Reporters/RelativePathReporter.swift diff --git a/Source/SwiftLintCore/Protocols/Reporter.swift b/Source/SwiftLintFramework/Reporters/Reporter.swift similarity index 100% rename from Source/SwiftLintCore/Protocols/Reporter.swift rename to Source/SwiftLintFramework/Reporters/Reporter.swift diff --git a/Source/SwiftLintCore/Reporters/SARIFReporter.swift b/Source/SwiftLintFramework/Reporters/SARIFReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/SARIFReporter.swift rename to Source/SwiftLintFramework/Reporters/SARIFReporter.swift diff --git a/Source/SwiftLintCore/Reporters/SonarQubeReporter.swift b/Source/SwiftLintFramework/Reporters/SonarQubeReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/SonarQubeReporter.swift rename to Source/SwiftLintFramework/Reporters/SonarQubeReporter.swift diff --git a/Source/SwiftLintCore/Reporters/SummaryReporter.swift b/Source/SwiftLintFramework/Reporters/SummaryReporter.swift similarity index 100% rename from Source/SwiftLintCore/Reporters/SummaryReporter.swift rename to Source/SwiftLintFramework/Reporters/SummaryReporter.swift diff --git a/Source/SwiftLintCore/Reporters/XcodeReporter.swift b/Source/SwiftLintFramework/Reporters/XcodeReporter.swift similarity index 70% rename from Source/SwiftLintCore/Reporters/XcodeReporter.swift rename to Source/SwiftLintFramework/Reporters/XcodeReporter.swift index a7a04913db..a40e0a5431 100644 --- a/Source/SwiftLintCore/Reporters/XcodeReporter.swift +++ b/Source/SwiftLintFramework/Reporters/XcodeReporter.swift @@ -16,13 +16,6 @@ struct XcodeReporter: Reporter { /// /// - returns: The report for a single violation. internal static func generateForSingleViolation(_ violation: StyleViolation) -> String { - // {full_path_to_file}{:line}{:character}: {error,warning}: {content} - [ - "\(violation.location): ", - "\(violation.severity.rawValue): ", - "\(violation.ruleName) Violation: ", - violation.reason, - " (\(violation.ruleIdentifier))", - ].joined() + violation.description } } diff --git a/Source/SwiftLintCore/Rules/CoreRules.swift b/Source/SwiftLintFramework/Rules/CoreRules.swift similarity index 100% rename from Source/SwiftLintCore/Rules/CoreRules.swift rename to Source/SwiftLintFramework/Rules/CoreRules.swift diff --git a/Source/SwiftLintCore/Rules/CustomRules.swift b/Source/SwiftLintFramework/Rules/CustomRules.swift similarity index 100% rename from Source/SwiftLintCore/Rules/CustomRules.swift rename to Source/SwiftLintFramework/Rules/CustomRules.swift diff --git a/Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift b/Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift similarity index 100% rename from Source/SwiftLintCore/Rules/SuperfluousDisableCommandRule.swift rename to Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift diff --git a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift b/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift index dc5cea9e4f..f3c7836772 100644 --- a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift @@ -1,4 +1,4 @@ -@testable import SwiftLintCore +@testable import SwiftLintFramework import SwiftLintTestHelpers import XCTest diff --git a/Tests/SwiftLintFrameworkTests/CommandTests.swift b/Tests/SwiftLintFrameworkTests/CommandTests.swift index 6ede31b63f..1859c369d1 100644 --- a/Tests/SwiftLintFrameworkTests/CommandTests.swift +++ b/Tests/SwiftLintFrameworkTests/CommandTests.swift @@ -2,7 +2,7 @@ import Foundation import SourceKittenFramework @testable import SwiftLintBuiltInRules -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest private extension Command { diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift index c20e8b90b5..a684fd68d5 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift @@ -1,4 +1,4 @@ -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest final class ConfigurationAliasesTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift index 5b530a3d13..02513c1d91 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift @@ -1,4 +1,4 @@ -import SwiftLintCore +import SwiftLintFramework // swiftlint:disable:next blanket_disable_command // swiftlint:disable nesting identifier_name diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift index e928221e9b..43dadadc62 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift @@ -1,5 +1,5 @@ @testable import SwiftLintBuiltInRules -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest // swiftlint:disable file_length diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index 9fd493a068..f88852c94c 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -1,6 +1,6 @@ import Foundation import SourceKittenFramework -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest // swiftlint:disable file_length diff --git a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift index 4a4f11006c..56f3e1f9bd 100644 --- a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift @@ -1,5 +1,6 @@ import SourceKittenFramework @testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest // swiftlint:disable file_length @@ -542,7 +543,7 @@ final class CustomRulesTests: SwiftLintTestCase { "only_rules": ["custom_rules", "superfluous_disable_command"], "custom_rules": customRules, ] - let configuration = try SwiftLintCore.Configuration(dict: configDict) + let configuration = try SwiftLintFramework.Configuration(dict: configDict) return SwiftLintTestHelpers.violations( example.skipWrappingInCommentTest(), config: configuration diff --git a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift b/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift index a95bb4ec5a..bb723d5321 100644 --- a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import SwiftLintFramework import XCTest final class ExpiringTodoRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/GlobTests.swift b/Tests/SwiftLintFrameworkTests/GlobTests.swift index 0f94aaddc2..04730dc09a 100644 --- a/Tests/SwiftLintFrameworkTests/GlobTests.swift +++ b/Tests/SwiftLintFrameworkTests/GlobTests.swift @@ -1,4 +1,4 @@ -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest final class GlobTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift b/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift index 3191a6a9f1..8abbff656f 100644 --- a/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift +++ b/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift @@ -1,5 +1,5 @@ import Foundation -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest private struct CacheTestHelper { diff --git a/Tests/SwiftLintFrameworkTests/ReporterTests.swift b/Tests/SwiftLintFrameworkTests/ReporterTests.swift index 252881c47f..b6c30be294 100644 --- a/Tests/SwiftLintFrameworkTests/ReporterTests.swift +++ b/Tests/SwiftLintFrameworkTests/ReporterTests.swift @@ -1,7 +1,7 @@ import Foundation import SourceKittenFramework @testable import SwiftLintBuiltInRules -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest final class ReporterTests: SwiftLintTestCase { @@ -199,7 +199,7 @@ final class ReporterTests: SwiftLintTestCase { with: FileManager.default.currentDirectoryPath ).replacingOccurrences( of: "${SWIFTLINT_VERSION}", - with: SwiftLintCore.Version.current.value + with: SwiftLintFramework.Version.current.value ).replacingOccurrences( of: "${TODAYS_DATE}", with: dateFormatter.string(from: Date()) @@ -213,7 +213,7 @@ final class ReporterTests: SwiftLintTestCase { of: FileManager.default.currentDirectoryPath, with: "${CURRENT_WORKING_DIRECTORY}" ).replacingOccurrences( - of: SwiftLintCore.Version.current.value, + of: SwiftLintFramework.Version.current.value, with: "${SWIFTLINT_VERSION}" ).replacingOccurrences( of: dateFormatter.string(from: Date()), diff --git a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift b/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift index 4b5b8d3e22..320ef5c47b 100644 --- a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift +++ b/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift @@ -1,4 +1,4 @@ -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest final class SourceKitCrashTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/YamlParserTests.swift b/Tests/SwiftLintFrameworkTests/YamlParserTests.swift index 4e36027c76..066d7610eb 100644 --- a/Tests/SwiftLintFrameworkTests/YamlParserTests.swift +++ b/Tests/SwiftLintFrameworkTests/YamlParserTests.swift @@ -1,4 +1,4 @@ -@testable import SwiftLintCore +@testable import SwiftLintFramework import XCTest final class YamlParserTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintTestHelpers/TestHelpers.swift b/Tests/SwiftLintTestHelpers/TestHelpers.swift index 06b09ac8b5..f729cadda9 100644 --- a/Tests/SwiftLintTestHelpers/TestHelpers.swift +++ b/Tests/SwiftLintTestHelpers/TestHelpers.swift @@ -1,6 +1,6 @@ import Foundation import SourceKittenFramework -import SwiftLintCore +import SwiftLintFramework import XCTest // swiftlint:disable file_length diff --git a/tools/get-version b/tools/get-version index 98e32344af..848fd0aeac 100755 --- a/tools/get-version +++ b/tools/get-version @@ -2,4 +2,4 @@ set -euo pipefail -cat Source/SwiftLintCore/Models/Version.swift | awk -F '"' '{print $2}' | xargs +cat Source/SwiftLintFramework/Models/Version.swift | awk -F '"' '{print $2}' | xargs From 15e1598d435e74cb5c4d4bcdf0f9d50c2743667f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 4 Mar 2024 22:12:26 +0100 Subject: [PATCH 119/260] Move reusable functionality from SwiftLintBuiltInRules to SwiftLintCore --- .../Metrics/FunctionBodyLengthRule.swift | 2 + .../Rules/Metrics/TypeBodyLengthRule.swift | 2 + .../Visitors/CodeBlockVisitor.swift | 105 ----------------- .../Visitors/BodyLengthRuleVisitor.swift | 31 +++-- .../Visitors/CodeBlockVisitor.swift | 108 ++++++++++++++++++ 5 files changed, 133 insertions(+), 115 deletions(-) delete mode 100644 Source/SwiftLintBuiltInRules/Visitors/CodeBlockVisitor.swift rename Source/{SwiftLintBuiltInRules => SwiftLintCore}/Visitors/BodyLengthRuleVisitor.swift (76%) create mode 100644 Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift diff --git a/Source/SwiftLintBuiltInRules/Rules/Metrics/FunctionBodyLengthRule.swift b/Source/SwiftLintBuiltInRules/Rules/Metrics/FunctionBodyLengthRule.swift index f0cb9ac56d..dc6eb5fe9f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Metrics/FunctionBodyLengthRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Metrics/FunctionBodyLengthRule.swift @@ -1,3 +1,5 @@ +import SwiftLintCore + struct FunctionBodyLengthRule: SwiftSyntaxRule { var configuration = SeverityLevelsConfiguration(warning: 50, error: 100) diff --git a/Source/SwiftLintBuiltInRules/Rules/Metrics/TypeBodyLengthRule.swift b/Source/SwiftLintBuiltInRules/Rules/Metrics/TypeBodyLengthRule.swift index 0c3da3c68b..08c1318a04 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Metrics/TypeBodyLengthRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Metrics/TypeBodyLengthRule.swift @@ -1,3 +1,5 @@ +import SwiftLintCore + private func wrapExample( prefix: String = "", _ type: String, diff --git a/Source/SwiftLintBuiltInRules/Visitors/CodeBlockVisitor.swift b/Source/SwiftLintBuiltInRules/Visitors/CodeBlockVisitor.swift deleted file mode 100644 index 07184901d7..0000000000 --- a/Source/SwiftLintBuiltInRules/Visitors/CodeBlockVisitor.swift +++ /dev/null @@ -1,105 +0,0 @@ -import SwiftLintCore -import SwiftSyntax - -class CodeBlockVisitor: ViolationsSyntaxVisitor { - override func visitPost(_ node: AccessorDeclSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: ActorDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: CatchClauseSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: ClassDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: ClosureExprSyntax) { - guard let parent = node.parent else { - return - } - if parent.is(LabeledExprSyntax.self) { - // Function parameter - return - } - if parent.is(FunctionCallExprSyntax.self) || parent.is(MultipleTrailingClosureElementSyntax.self), - node.keyPathInParent != \FunctionCallExprSyntax.calledExpression { - // Trailing closure - collectViolations(for: node) - } - } - - override func visitPost(_ node: DeferStmtSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: DoStmtSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: EnumDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: ExtensionDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: FunctionDeclSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: ForStmtSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: GuardStmtSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: IfExprSyntax) { - collectViolations(for: node.body) - if case let .codeBlock(body) = node.elseBody { - collectViolations(for: body) - } - } - override func visitPost(_ node: InitializerDeclSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: PatternBindingSyntax) { - collectViolations(for: node.accessorBlock) - } - - override func visitPost(_ node: PrecedenceGroupDeclSyntax) { - collectViolations(for: node) - } - - override func visitPost(_ node: ProtocolDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: RepeatStmtSyntax) { - collectViolations(for: node.body) - } - - override func visitPost(_ node: StructDeclSyntax) { - collectViolations(for: node.memberBlock) - } - - override func visitPost(_ node: SwitchExprSyntax) { - collectViolations(for: node) - } - - override func visitPost(_ node: WhileStmtSyntax) { - collectViolations(for: node.body) - } - - func collectViolations(for _: (some BracedSyntax)?) { - // Intended to be overridden. - } -} diff --git a/Source/SwiftLintBuiltInRules/Visitors/BodyLengthRuleVisitor.swift b/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift similarity index 76% rename from Source/SwiftLintBuiltInRules/Visitors/BodyLengthRuleVisitor.swift rename to Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift index 4800641f77..0c685a61d5 100644 --- a/Source/SwiftLintBuiltInRules/Visitors/BodyLengthRuleVisitor.swift +++ b/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift @@ -1,11 +1,16 @@ import SwiftSyntax -final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor> { +/// Visitor that collection violations of code block lengths. +public final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor> { private let kind: Kind - enum Kind { + /// The code block types to check. + public enum Kind { + /// Closure code blocks. case closure + /// Function body blocks. case function + /// Type (class, enum, ...) member blocks. case type fileprivate var name: String { @@ -20,12 +25,18 @@ final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor) { + /// Initializer. + /// + /// - Parameters: + /// - kind: The code block type to check. See ``Kind``. + /// - file: The file to collect violation for. + /// - configuration: The configuration that defines the acceptable limits. + public init(kind: Kind, file: SwiftLintFile, configuration: SeverityLevelsConfiguration) { self.kind = kind super.init(configuration: configuration, file: file) } - override func visitPost(_ node: EnumDeclSyntax) { + override public func visitPost(_ node: EnumDeclSyntax) { if kind == .type { registerViolations( leftBrace: node.memberBlock.leftBrace, @@ -35,7 +46,7 @@ final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor: ViolationsSyntaxVisitor { + override open func visitPost(_ node: AccessorDeclSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: ActorDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: CatchClauseSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: ClassDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: ClosureExprSyntax) { + guard let parent = node.parent else { + return + } + if parent.is(LabeledExprSyntax.self) { + // Function parameter + return + } + if parent.is(FunctionCallExprSyntax.self) || parent.is(MultipleTrailingClosureElementSyntax.self), + node.keyPathInParent != \FunctionCallExprSyntax.calledExpression { + // Trailing closure + collectViolations(for: node) + } + } + + override open func visitPost(_ node: DeferStmtSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: DoStmtSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: EnumDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: ExtensionDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: FunctionDeclSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: ForStmtSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: GuardStmtSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: IfExprSyntax) { + collectViolations(for: node.body) + if case let .codeBlock(body) = node.elseBody { + collectViolations(for: body) + } + } + override open func visitPost(_ node: InitializerDeclSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: PatternBindingSyntax) { + collectViolations(for: node.accessorBlock) + } + + override open func visitPost(_ node: PrecedenceGroupDeclSyntax) { + collectViolations(for: node) + } + + override open func visitPost(_ node: ProtocolDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: RepeatStmtSyntax) { + collectViolations(for: node.body) + } + + override open func visitPost(_ node: StructDeclSyntax) { + collectViolations(for: node.memberBlock) + } + + override open func visitPost(_ node: SwitchExprSyntax) { + collectViolations(for: node) + } + + override open func visitPost(_ node: WhileStmtSyntax) { + collectViolations(for: node.body) + } + + /// Collects violations for the given braced item. Intended to be specialized by subclasses. + /// + /// - Parameter bracedItem: The braced item to collect violations for. + open func collectViolations(for _: (some BracedSyntax)?) { + // Intended to be overridden. + } +} From 610722ccb231082371f8a21997293ae1a532bb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 9 Mar 2024 16:51:49 +0100 Subject: [PATCH 120/260] Disable Strict Concurrency for the time being --- BUILD | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD b/BUILD index f6e1b93ccf..13b099db4d 100644 --- a/BUILD +++ b/BUILD @@ -143,7 +143,7 @@ swift_library( srcs = glob( ["Source/SwiftLintFramework/**/*.swift"], ), - copts = copts + strict_concurrency_copts, + copts = copts, # TODO: strict_concurrency_copts module_name = "SwiftLintFramework", visibility = ["//visibility:public"], deps = [ diff --git a/Package.swift b/Package.swift index 85cdce9d31..7538712987 100644 --- a/Package.swift +++ b/Package.swift @@ -107,7 +107,7 @@ let package = Package( "SwiftLintExtraRules", "CollectionConcurrencyKit", ], - swiftSettings: swiftFeatures + strictConcurrency + swiftSettings: swiftFeatures ), .target(name: "DyldWarningWorkaround"), .target( From cbedb0844ecc3f103e64cfa545d7d663c6d4245e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 23 Dec 2024 15:18:35 +0100 Subject: [PATCH 121/260] Inline custom visitors (#5909) --- Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift | 3 ++- Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift b/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift index 0c685a61d5..22e985a65a 100644 --- a/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift +++ b/Source/SwiftLintCore/Visitors/BodyLengthRuleVisitor.swift @@ -2,7 +2,7 @@ import SwiftSyntax /// Visitor that collection violations of code block lengths. public final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor> { - private let kind: Kind + @usableFromInline let kind: Kind /// The code block types to check. public enum Kind { @@ -31,6 +31,7 @@ public final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor< /// - kind: The code block type to check. See ``Kind``. /// - file: The file to collect violation for. /// - configuration: The configuration that defines the acceptable limits. + @inlinable public init(kind: Kind, file: SwiftLintFile, configuration: SeverityLevelsConfiguration) { self.kind = kind super.init(configuration: configuration, file: file) diff --git a/Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift b/Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift index 6a4ac213c9..71752dd3ba 100644 --- a/Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift +++ b/Source/SwiftLintCore/Visitors/CodeBlockVisitor.swift @@ -2,6 +2,11 @@ import SwiftSyntax /// A visitor that collects style violations for all available code blocks. open class CodeBlockVisitor: ViolationsSyntaxVisitor { + @inlinable + override public init(configuration: Configuration, file: SwiftLintFile) { + super.init(configuration: configuration, file: file) + } + override open func visitPost(_ node: AccessorDeclSyntax) { collectViolations(for: node.body) } From 532bc5a7703a1abb98154ed222b1bbc17f641d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 23 Dec 2024 16:40:21 +0100 Subject: [PATCH 122/260] Remove option for strict concurrency checking (#5910) --- .buildkite/pipeline.yml | 6 ------ BUILD | 5 ----- 2 files changed, 11 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 22fb23aafe..e3c492c64d 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -5,12 +5,6 @@ steps: - bazel build :swiftlint - echo "+++ Test" - bazel test --test_output=errors //Tests/... - - label: "Build With Strict Concurrency" - commands: - - echo "+++ Build" - - bazel build --define strict_concurrency_builtin_rules=true :swiftlint - - echo "--- Clean up" - - git reset --hard - label: "SwiftPM" commands: - echo "+++ Test" diff --git a/BUILD b/BUILD index 13b099db4d..ef07adeba4 100644 --- a/BUILD +++ b/BUILD @@ -8,11 +8,6 @@ load( "universal_swift_compiler_plugin", ) -config_setting( - name = "strict_concurrency_builtin_rules", - values = {"define": "strict_concurrency_builtin_rules=true"}, -) - bool_flag( name = "universal_tools", build_setting_default = False, From 4189010afb8bd04c9d2e5f9a38fe44808e6727ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 23 Dec 2024 16:51:16 +0100 Subject: [PATCH 123/260] Update CryptoSwift with support for Musl C library (#5911) --- Package.resolved | 4 ++-- Package.swift | 2 +- bazel/repos.bzl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Package.resolved b/Package.resolved index ee63d9748b..baa9895fe5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", "state" : { - "revision" : "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0", - "version" : "1.8.2" + "revision" : "729e01bc9b9dab466ac85f21fb9ee2bc1c61b258", + "version" : "1.8.4" } }, { diff --git a/Package.swift b/Package.swift index 7538712987..934c1f1e1a 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,7 @@ let package = Package( .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.2.0"), - .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.8.0")), + .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.8.4")), ], targets: [ .plugin( diff --git a/bazel/repos.bzl b/bazel/repos.bzl index 447e216d8b..47f2aa9d39 100644 --- a/bazel/repos.bzl +++ b/bazel/repos.bzl @@ -60,8 +60,8 @@ def swiftlint_repos(bzlmod = False): name = "com_github_krzyzanowskim_cryptoswift", sha256 = "3d649cccfe9ae0572163cde0201f013d10349a035c15225e7a4bd83c85fb0d1d", build_file = "@SwiftLint//bazel:CryptoSwift.BUILD", - strip_prefix = "CryptoSwift-1.8.0", - url = "https://github.com/krzyzanowskim/CryptoSwift/archive/refs/tags/1.8.0.tar.gz", + strip_prefix = "CryptoSwift-1.8.4", + url = "https://github.com/krzyzanowskim/CryptoSwift/archive/refs/tags/1.8.4.tar.gz", ) def _swiftlint_repos_bzlmod(_): From 1a54e3e31326327d809a0a29c2e1c02c28445781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 18 Nov 2024 23:49:26 +0100 Subject: [PATCH 124/260] Capture console on main actor only --- Source/SwiftLintCore/Models/Issue.swift | 31 +++++++---- ...licitTypeInterfaceConfigurationTests.swift | 7 +-- .../IndentationWidthRuleTests.swift | 7 +-- ...ultilineParametersConfigurationTests.swift | 12 ++--- .../NoEmptyBlockConfigurationTests.swift | 7 +-- .../RuleConfigurationDescriptionTests.swift | 23 ++++---- .../SwiftLintTestCase.swift | 52 +++++++++++++++++++ 7 files changed, 105 insertions(+), 34 deletions(-) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index d09affb2d3..bed6671aaa 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -92,15 +92,24 @@ public enum Issue: LocalizedError, Equatable { /// - parameter runner: The code to run. Messages printed during the execution are collected. /// /// - returns: The collected messages produced while running the code in the runner. - static func captureConsole(runner: () throws -> Void) rethrows -> String { - var console = "" - messageConsumer = { console += (console.isEmpty ? "" : "\n") + $0 } - defer { messageConsumer = nil } + @MainActor + static func captureConsole(runner: () throws -> Void) async rethrows -> String { + actor Console { + static var content = "" + } + messageConsumer = { + Console.content += (Console.content.isEmpty ? "" : "\n") + $0 + } + defer { + messageConsumer = nil + Console.content = "" + } try runner() - return console + await Task.yield() + return Console.content } - private static var messageConsumer: ((String) -> Void)? + @MainActor private static var messageConsumer: (@Sendable (String) -> Void)? /// Wraps any `Error` into a `SwiftLintError.genericWarning` if it is not already a `SwiftLintError`. /// @@ -133,10 +142,12 @@ public enum Issue: LocalizedError, Equatable { if case .ruleDeprecated = self, !Self.printDeprecationWarnings { return } - if let consumer = Self.messageConsumer { - consumer(errorDescription) - } else { - queuedPrintError(errorDescription) + Task { @MainActor in + if let consumer = Self.messageConsumer { + consumer(errorDescription) + } else { + queuedPrintError(errorDescription) + } } } diff --git a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift index ec413e59a4..df4d43731c 100644 --- a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift @@ -23,10 +23,11 @@ final class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase { XCTAssertTrue(config.allowRedundancy) } - func testInvalidKeyInCustomConfiguration() { + @MainActor + func testInvalidKeyInCustomConfiguration() async throws { var config = ExplicitTypeInterfaceConfiguration() - XCTAssertEqual( - try Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, + try await AsyncAssertEqual( + try await Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, "warning: Configuration for 'explicit_type_interface' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift b/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift index b0234978bc..4946e63adc 100644 --- a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift @@ -4,13 +4,14 @@ import SwiftLintTestHelpers import XCTest final class IndentationWidthRuleTests: SwiftLintTestCase { - func testInvalidIndentation() throws { + @MainActor + func testInvalidIndentation() async throws { var testee = IndentationWidthConfiguration() let defaultValue = testee.indentationWidth for indentation in [0, -1, -5] { - XCTAssertEqual( - try Issue.captureConsole { try testee.apply(configuration: ["indentation_width": indentation]) }, + try await AsyncAssertEqual( + try await Issue.captureConsole { try testee.apply(configuration: ["indentation_width": indentation]) }, "warning: Invalid configuration for 'indentation_width' rule. Falling back to default." ) // Value remains the default. diff --git a/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift index 2114d6409e..ecea8d2c40 100644 --- a/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift @@ -3,12 +3,12 @@ import XCTest final class MultilineParametersConfigurationTests: SwiftLintTestCase { - func testInvalidMaxNumberOfSingleLineParameters() throws { + func testInvalidMaxNumberOfSingleLineParameters() async throws { for maxNumberOfSingleLineParameters in [0, -1] { var config = MultilineParametersConfiguration() - XCTAssertEqual( - try Issue.captureConsole { + try await AsyncAssertEqual( + try await Issue.captureConsole { try config.apply( configuration: ["max_number_of_single_line_parameters": maxNumberOfSingleLineParameters] ) @@ -21,11 +21,11 @@ final class MultilineParametersConfigurationTests: SwiftLintTestCase { } } - func testInvalidMaxNumberOfSingleLineParametersWithSingleLineEnabled() throws { + func testInvalidMaxNumberOfSingleLineParametersWithSingleLineEnabled() async throws { var config = MultilineParametersConfiguration() - XCTAssertEqual( - try Issue.captureConsole { + try await AsyncAssertEqual( + try await Issue.captureConsole { try config.apply( configuration: ["max_number_of_single_line_parameters": 2, "allows_single_line": false] ) diff --git a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift index 22b22f58d6..9a88de66aa 100644 --- a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift @@ -21,10 +21,11 @@ final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { XCTAssertEqual(config.enabledBlockTypes, Set([.initializerBodies, .statementBlocks, .closureBlocks])) } - func testInvalidKeyInCustomConfiguration() { + @MainActor + func testInvalidKeyInCustomConfiguration() async throws { var config = NoEmptyBlockConfiguration() - XCTAssertEqual( - try Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, + try await AsyncAssertEqual( + try await Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, "warning: Configuration for 'no_empty_block' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift b/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift index a656666d60..c3a00722fd 100644 --- a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift +++ b/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift @@ -5,7 +5,7 @@ import XCTest // swiftlint:disable file_length // swiftlint:disable:next type_body_length -final class RuleConfigurationDescriptionTests: XCTestCase { +final class RuleConfigurationDescriptionTests: SwiftLintTestCase { @AutoConfigParser private struct TestConfiguration: RuleConfiguration { typealias Parent = RuleMock // swiftlint:disable:this nesting @@ -490,26 +490,31 @@ final class RuleConfigurationDescriptionTests: XCTestCase { XCTAssertEqual(configuration.nestedSeverityLevels, SeverityLevelsConfiguration(warning: 6, error: 7)) } - func testDeprecationWarning() throws { + @MainActor + func testDeprecationWarning() async throws { var configuration = TestConfiguration() - XCTAssertEqual( - try Issue.captureConsole { try configuration.apply(configuration: ["set": [6, 7]]) }, + try await AsyncAssertEqual( + try await Issue.captureConsole { try configuration.apply(configuration: ["set": [6, 7]]) }, "warning: Configuration option 'set' in 'my_rule' rule is deprecated. Use the option 'other_opt' instead." ) } - func testNoDeprecationWarningIfNoDeprecatedPropertySet() throws { + @MainActor + func testNoDeprecationWarningIfNoDeprecatedPropertySet() async throws { var configuration = TestConfiguration() - XCTAssert(try Issue.captureConsole { try configuration.apply(configuration: ["flag": false]) }.isEmpty) + try await AsyncAssertTrue( + try await Issue.captureConsole { try configuration.apply(configuration: ["flag": false]) }.isEmpty + ) } - func testInvalidKeys() throws { + @MainActor + func testInvalidKeys() async throws { var configuration = TestConfiguration() - XCTAssertEqual( - try Issue.captureConsole { + try await AsyncAssertEqual( + try await Issue.captureConsole { try configuration.apply(configuration: [ "severity": "error", "warning": 3, diff --git a/Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift b/Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift index ad8b591e3b..3a99c513fd 100644 --- a/Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift +++ b/Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift @@ -1,10 +1,62 @@ import SwiftLintFramework import XCTest +// swiftlint:disable:next blanket_disable_command +// swiftlint:disable test_case_accessibility + // swiftlint:disable:next balanced_xctest_lifecycle open class SwiftLintTestCase: XCTestCase { override open class func setUp() { super.setUp() RuleRegistry.registerAllRulesOnce() } + + // swiftlint:disable:next identifier_name + public func AsyncAssertFalse(_ condition: @autoclosure () async -> Bool, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) async { + let condition = await condition() + XCTAssertFalse(condition, message(), file: file, line: line) + } + + // swiftlint:disable:next identifier_name + public func AsyncAssertTrue(_ condition: @autoclosure () async throws -> Bool, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) async rethrows { + let condition = try await condition() + XCTAssertTrue(condition, message(), file: file, line: line) + } + + // swiftlint:disable:next identifier_name + public func AsyncAssertEqual(_ expression1: @autoclosure () async throws -> T, + _ expression2: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) async rethrows { + let value1 = try await expression1() + let value2 = try await expression2() + XCTAssertEqual(value1, value2, message(), file: file, line: line) + } + + // swiftlint:disable:next identifier_name + public func AsyncAssertNotEqual(_ expression1: @autoclosure () async -> T, + _ expression2: @autoclosure () async -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) async { + let value1 = await expression1() + let value2 = await expression2() + XCTAssertNotEqual(value1, value2, message(), file: file, line: line) + } + + // swiftlint:disable:next identifier_name + public func AsyncAssertNil(_ expression: @autoclosure () async -> T?, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) async { + let value = await expression() + XCTAssertNil(value, message(), file: file, line: line) + } } From d14d40b80a643a0fd7d0ca914ee9fc5dfa64e538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 18:50:26 +0100 Subject: [PATCH 125/260] Disable Swift 5 build --- CHANGELOG.md | 3 +++ azure-pipelines.yml | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa561b330..9a283f0cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ * The `inert_defer` and `unused_capture_list` rules have completely been removed after being deprecated for 2 years. [SimplyDanny](https://github.com/SimplyDanny) +* SwiftLint now requires a Swift 6 or higher compiler to build. + [SimplyDanny](https://github.com/SimplyDanny) + #### Experimental * None. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6fc1eb3f11..3ec9125914 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,9 +19,6 @@ jobs: strategy: maxParallel: 10 matrix: - '13': - image: 'macOS-13' - xcode: '15.2' '14': image: 'macOS-14' xcode: '15.4' From ec35f95786cbff236c6adfb0512fe41ab1d67e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 17 Nov 2024 16:23:55 +0100 Subject: [PATCH 126/260] Fix strict concurrency warnings in SwiftLintCore --- .../Extensions/Array+SwiftLint.swift | 29 +++++++++++++----- .../NSRegularExpression+SwiftLint.swift | 4 +-- ...ftDeclarationAttributeKind+Swiftlint.swift | 2 +- .../SwiftDeclarationKind+SwiftLint.swift | 2 +- .../Extensions/SwiftLintFile+Cache.swift | 17 +++++------ .../Extensions/SyntaxKind+SwiftLint.swift | 2 +- .../ChildOptionSeverityConfiguration.swift | 4 ++- Source/SwiftLintCore/Models/Region.swift | 2 +- .../Models/RuleConfigurationDescription.swift | 18 ++++++----- .../SwiftLintCore/Models/RuleIdentifier.swift | 2 +- .../Models/SeverityConfiguration.swift | 2 +- .../SwiftLintCore/Models/SwiftLintFile.swift | 30 +++++++++---------- .../Models/ViolationSeverity.swift | 2 +- .../SeverityLevelsConfiguration.swift | 2 +- .../Models/CustomRuleTimer.swift | 16 +++++----- .../SwiftLintFramework/Models/Version.swift | 2 +- .../Rules/SuperfluousDisableCommandRule.swift | 2 +- Tests/SwiftLintTestHelpers/TestHelpers.swift | 9 ++---- 18 files changed, 81 insertions(+), 66 deletions(-) diff --git a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift index 9981e91bdd..89eda406f6 100644 --- a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift @@ -82,7 +82,7 @@ public extension Array { /// - parameter transform: The transformation to apply to each element. /// /// - returns: The result of applying `transform` on every element and flattening the results. - func parallelFlatMap(transform: (Element) -> [T]) -> [T] { + func parallelFlatMap(transform: @Sendable (Element) -> [T]) -> [T] { parallelMap(transform: transform).flatMap { $0 } } @@ -91,7 +91,7 @@ public extension Array { /// - parameter transform: The transformation to apply to each element. /// /// - returns: The result of applying `transform` on every element and discarding the `nil` ones. - func parallelCompactMap(transform: (Element) -> T?) -> [T] { + func parallelCompactMap(transform: @Sendable (Element) -> T?) -> [T] { parallelMap(transform: transform).compactMap { $0 } } @@ -100,18 +100,21 @@ public extension Array { /// - parameter transform: The transformation to apply to each element. /// /// - returns: The result of applying `transform` on every element. - func parallelMap(transform: (Element) -> T) -> [T] { + func parallelMap(transform: @Sendable (Element) -> T) -> [T] { var result = ContiguousArray(repeating: nil, count: count) return result.withUnsafeMutableBufferPointer { buffer in - let buffer = Wrapper(buffer: buffer) - DispatchQueue.concurrentPerform(iterations: buffer.count) { idx in - buffer[idx] = transform(self[idx]) + let buffer = MutableWrapper(buffer: buffer) + withUnsafeBufferPointer { array in + let array = ImmutableWrapper(buffer: array) + DispatchQueue.concurrentPerform(iterations: buffer.count) { idx in + buffer[idx] = transform(array[idx]) + } } return buffer.data } } - private class Wrapper: @unchecked Sendable { + private class MutableWrapper: @unchecked Sendable { let buffer: UnsafeMutableBufferPointer init(buffer: UnsafeMutableBufferPointer) { @@ -135,6 +138,18 @@ public extension Array { } } } + + private class ImmutableWrapper: @unchecked Sendable { + let buffer: UnsafeBufferPointer + + init(buffer: UnsafeBufferPointer) { + self.buffer = buffer + } + + subscript(index: Int) -> T { + buffer[index] + } + } } public extension Collection { diff --git a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift index 417f4da6f2..fc482a379d 100644 --- a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift @@ -1,10 +1,10 @@ import Foundation import SourceKittenFramework -private var regexCache = [RegexCacheKey: NSRegularExpression]() private let regexCacheLock = NSLock() +private nonisolated(unsafe) var regexCache = [RegexCacheKey: NSRegularExpression]() -public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLiteral { +public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLiteral, Sendable { public let regex: NSRegularExpression public init(pattern: String, options _: NSRegularExpression.Options? = nil) throws { diff --git a/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift b/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift index 49895e6e80..e06d10eefc 100644 --- a/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift +++ b/Source/SwiftLintCore/Extensions/SwiftDeclarationAttributeKind+Swiftlint.swift @@ -10,7 +10,7 @@ public extension SwiftDeclarationAttributeKind { ] } - enum ModifierGroup: String, CustomDebugStringConvertible { + enum ModifierGroup: String, CustomDebugStringConvertible, Sendable { case `override` case acl case setterACL diff --git a/Source/SwiftLintCore/Extensions/SwiftDeclarationKind+SwiftLint.swift b/Source/SwiftLintCore/Extensions/SwiftDeclarationKind+SwiftLint.swift index 17cd5f2b51..2e99c696ea 100644 --- a/Source/SwiftLintCore/Extensions/SwiftDeclarationKind+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/SwiftDeclarationKind+SwiftLint.swift @@ -1,4 +1,4 @@ -import SourceKittenFramework +@preconcurrency import SourceKittenFramework public extension SwiftDeclarationKind { static let variableKinds: Set = [ diff --git a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift index 8f69627099..61a5d63302 100644 --- a/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift +++ b/Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift @@ -53,17 +53,16 @@ private let linesWithTokensCache = Cache { $0.computeLinesWithTokens() } package typealias AssertHandler = () -> Void // Re-enable once all parser diagnostics in tests have been addressed. // https://github.com/realm/SwiftLint/issues/3348 -package var parserDiagnosticsDisabledForTests = false +package nonisolated(unsafe) var parserDiagnosticsDisabledForTests = false -private let assertHandlers = [FileCacheKey: AssertHandler]() -private let assertHandlerCache = Cache { file in assertHandlers[file.cacheKey] } +private let assertHandlerCache = Cache { (_: SwiftLintFile) -> AssertHandler? in nil } -private class Cache { - private var values = [FileCacheKey: T]() - private let factory: (SwiftLintFile) -> T +private final class Cache: Sendable { + private nonisolated(unsafe) var values = [FileCacheKey: T]() + private let factory: @Sendable (SwiftLintFile) -> T private let lock = PlatformLock() - fileprivate init(_ factory: @escaping (SwiftLintFile) -> T) { + fileprivate init(_ factory: @escaping @Sendable (SwiftLintFile) -> T) { self.factory = factory } @@ -224,9 +223,9 @@ extension SwiftLintFile { } } -private final class PlatformLock { +private final class PlatformLock: Sendable { #if canImport(Darwin) - private let primitiveLock: UnsafeMutablePointer + private nonisolated(unsafe) let primitiveLock: UnsafeMutablePointer #else private let primitiveLock = NSLock() #endif diff --git a/Source/SwiftLintCore/Extensions/SyntaxKind+SwiftLint.swift b/Source/SwiftLintCore/Extensions/SyntaxKind+SwiftLint.swift index 8099f0ca37..60a0ab6014 100644 --- a/Source/SwiftLintCore/Extensions/SyntaxKind+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/SyntaxKind+SwiftLint.swift @@ -1,4 +1,4 @@ -import SourceKittenFramework +@preconcurrency import SourceKittenFramework public extension SyntaxKind { init?(shortName: Swift.String) { diff --git a/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift b/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift index 4f63881029..f0f28828e5 100644 --- a/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift +++ b/Source/SwiftLintCore/Models/ChildOptionSeverityConfiguration.swift @@ -1,6 +1,8 @@ /// A rule configuration that allows to disable (`off`) an option of a rule or specify its severity level in which /// case it's active. -public struct ChildOptionSeverityConfiguration: RuleConfiguration, AcceptableByConfigurationElement { +public struct ChildOptionSeverityConfiguration: RuleConfiguration, + AcceptableByConfigurationElement, + Sendable { /// Configuration with a warning severity. public static var error: Self { Self(optionSeverity: .error) } /// Configuration with an error severity. diff --git a/Source/SwiftLintCore/Models/Region.swift b/Source/SwiftLintCore/Models/Region.swift index 9c6483fbd9..6db3359aac 100644 --- a/Source/SwiftLintCore/Models/Region.swift +++ b/Source/SwiftLintCore/Models/Region.swift @@ -1,7 +1,7 @@ import SwiftSyntax /// A contiguous region of Swift source code. -public struct Region: Equatable { +public struct Region: Equatable, Sendable { /// The location describing the start of the region. All locations that are less than this value /// (earlier in the source file) are not contained in this region. public let start: Location diff --git a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift index c3636b3c29..1cc140fe31 100644 --- a/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift +++ b/Source/SwiftLintCore/Models/RuleConfigurationDescription.swift @@ -24,7 +24,7 @@ public protocol Documentable { } /// Description of a rule configuration. -public struct RuleConfigurationDescription: Equatable { +public struct RuleConfigurationDescription: Equatable, Sendable { fileprivate let options: [RuleConfigurationOption] fileprivate init(options: [RuleConfigurationOption], exclusiveOptions: Set = []) { @@ -122,7 +122,7 @@ extension RuleConfigurationDescription: Documentable { } /// A single option of a ``RuleConfigurationDescription``. -public struct RuleConfigurationOption: Equatable { +public struct RuleConfigurationOption: Equatable, Sendable { /// An option serving as a marker for an empty configuration description. public static let noOptions = Self(key: "", value: .empty) @@ -164,7 +164,7 @@ extension RuleConfigurationOption: Documentable { } /// Type of an option. -public enum OptionType: Equatable { +public enum OptionType: Equatable, Sendable { /// An irrelevant option. It will be ignored in documentation serialization. case empty /// A boolean flag. @@ -415,9 +415,9 @@ public protocol InlinableOptionType: AcceptableByConfigurationElement {} /// ``` /// @propertyWrapper -public struct ConfigurationElement: Equatable { +public struct ConfigurationElement: Equatable, Sendable { /// A deprecation notice. - public enum DeprecationNotice { + public enum DeprecationNotice: Sendable { /// Warning suggesting an alternative option. case suggestAlternative(ruleID: String, name: String) } @@ -448,7 +448,7 @@ public struct ConfigurationElement Void + private let postprocessor: @Sendable (inout T) -> Void /// Default constructor. /// @@ -461,7 +461,8 @@ public struct ConfigurationElement Void = { _ in }) { // swiftlint:disable:this no_empty_block + postprocessor: @escaping @Sendable (inout T) -> Void = { _ in }) { + // swiftlint:disable:previous no_empty_block self.init( wrappedValue: value, key: key, @@ -509,7 +510,8 @@ public struct ConfigurationElement Void = { _ in }) { // swiftlint:disable:this no_empty_block + postprocessor: @escaping @Sendable (inout T) -> Void = { _ in }) { + // swiftlint:disable:previous no_empty_block self.wrappedValue = wrappedValue self.key = key self.inline = inline diff --git a/Source/SwiftLintCore/Models/RuleIdentifier.swift b/Source/SwiftLintCore/Models/RuleIdentifier.swift index 8955c8badf..5fd1a692f1 100644 --- a/Source/SwiftLintCore/Models/RuleIdentifier.swift +++ b/Source/SwiftLintCore/Models/RuleIdentifier.swift @@ -1,5 +1,5 @@ /// An identifier representing a SwiftLint rule, or all rules. -public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral, Comparable { +public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral, Comparable, Sendable { // MARK: - Values /// Special identifier that should be treated as referring to 'all' SwiftLint rules. One helpful usecase is in diff --git a/Source/SwiftLintCore/Models/SeverityConfiguration.swift b/Source/SwiftLintCore/Models/SeverityConfiguration.swift index 83336a37e7..705801e5b7 100644 --- a/Source/SwiftLintCore/Models/SeverityConfiguration.swift +++ b/Source/SwiftLintCore/Models/SeverityConfiguration.swift @@ -1,5 +1,5 @@ /// A rule configuration that allows specifying the desired severity level for violations. -public struct SeverityConfiguration: SeverityBasedRuleConfiguration, InlinableOptionType { +public struct SeverityConfiguration: SeverityBasedRuleConfiguration, InlinableOptionType, Sendable { /// Configuration with a warning severity. public static var error: Self { Self(.error) } /// Configuration with an error severity. diff --git a/Source/SwiftLintCore/Models/SwiftLintFile.swift b/Source/SwiftLintCore/Models/SwiftLintFile.swift index 7a42fb0936..4809ed6efc 100644 --- a/Source/SwiftLintCore/Models/SwiftLintFile.swift +++ b/Source/SwiftLintCore/Models/SwiftLintFile.swift @@ -1,32 +1,37 @@ import Foundation -import SourceKittenFramework +@preconcurrency import SourceKittenFramework /// A unit of Swift source code, either on disk or in memory. -public final class SwiftLintFile { +public final class SwiftLintFile: Sendable { /// The underlying SourceKitten file. public let file: File /// The associated unique identifier for this file. public let id: UUID /// Whether or not this is a file generated for testing purposes. - public private(set) var isTestFile = false + public let isTestFile: Bool /// A file is virtual if it is not backed by a filesystem path. - public private(set) var isVirtual = false + public let isVirtual: Bool /// Creates a `SwiftLintFile` with a SourceKitten `File`. /// /// - parameter file: A file from SourceKitten. - public init(file: File) { + /// - parameter isTestFile: Mark the file as being generated for testing purposes only. + /// - parameter isVirtual: Mark the file as virtual (in-memory). + public init(file: File, isTestFile: Bool = false, isVirtual: Bool = false) { self.file = file self.id = UUID() + self.isTestFile = isTestFile + self.isVirtual = isVirtual } /// Creates a `SwiftLintFile` by specifying its path on disk. /// Fails if the file does not exist. /// /// - parameter path: The path to a file on disk. Relative and absolute paths supported. - public convenience init?(path: String) { + /// - parameter isTestFile: Mark the file as being generated for testing purposes only. + public convenience init?(path: String, isTestFile: Bool = false) { guard let file = File(path: path) else { return nil } - self.init(file: file) + self.init(file: file, isTestFile: isTestFile) } /// Creates a `SwiftLintFile` by specifying its path on disk. Unlike the `SwiftLintFile(path:)` initializer, this @@ -40,9 +45,9 @@ public final class SwiftLintFile { /// Creates a `SwiftLintFile` that is not backed by a file on disk by specifying its contents. /// /// - parameter contents: The contents of the file. - public convenience init(contents: String) { - self.init(file: File(contents: contents)) - isVirtual = true + /// - parameter isTestFile: Mark the file as being generated for testing purposes only. + public convenience init(contents: String, isTestFile: Bool = false) { + self.init(file: File(contents: contents), isTestFile: isTestFile, isVirtual: true) } /// The path on disk for this file. @@ -64,11 +69,6 @@ public final class SwiftLintFile { public var lines: [Line] { file.lines } - - /// Mark this file as used for testing purposes. - package func markAsTestFile() { - isTestFile = true - } } // MARK: - Hashable Conformance diff --git a/Source/SwiftLintCore/Models/ViolationSeverity.swift b/Source/SwiftLintCore/Models/ViolationSeverity.swift index 41c75abb03..a833de23e9 100644 --- a/Source/SwiftLintCore/Models/ViolationSeverity.swift +++ b/Source/SwiftLintCore/Models/ViolationSeverity.swift @@ -1,6 +1,6 @@ /// The magnitude of a `StyleViolation`. @AcceptableByConfigurationElement -public enum ViolationSeverity: String, Comparable, Codable, InlinableOptionType { +public enum ViolationSeverity: String, Comparable, Codable, Sendable, InlinableOptionType { /// Non-fatal. If using SwiftLint as an Xcode build phase, Xcode will mark the build as having succeeded. case warning /// Fatal. If using SwiftLint as an Xcode build phase, Xcode will mark the build as having failed. diff --git a/Source/SwiftLintCore/RuleConfigurations/SeverityLevelsConfiguration.swift b/Source/SwiftLintCore/RuleConfigurations/SeverityLevelsConfiguration.swift index 4761a0f011..e23f082b05 100644 --- a/Source/SwiftLintCore/RuleConfigurations/SeverityLevelsConfiguration.swift +++ b/Source/SwiftLintCore/RuleConfigurations/SeverityLevelsConfiguration.swift @@ -1,5 +1,5 @@ /// A rule configuration that allows specifying thresholds for `warning` and `error` severities. -public struct SeverityLevelsConfiguration: RuleConfiguration, InlinableOptionType { +public struct SeverityLevelsConfiguration: RuleConfiguration, InlinableOptionType, Sendable { /// The threshold for a violation to be a warning. @ConfigurationElement(key: "warning") public var warning = 12 diff --git a/Source/SwiftLintFramework/Models/CustomRuleTimer.swift b/Source/SwiftLintFramework/Models/CustomRuleTimer.swift index af131bb261..6d072f4f97 100644 --- a/Source/SwiftLintFramework/Models/CustomRuleTimer.swift +++ b/Source/SwiftLintFramework/Models/CustomRuleTimer.swift @@ -1,7 +1,7 @@ import Foundation /// Utility to measure the time spent in each custom rule. -public final class CustomRuleTimer { +public final class CustomRuleTimer: @unchecked Sendable { private let lock = NSLock() private var ruleIDForTimes = [String: [TimeInterval]]() private var shouldRecord = false @@ -16,7 +16,9 @@ public final class CustomRuleTimer { /// Return all time spent for each custom rule, keyed by rule ID. public func dump() -> [String: TimeInterval] { - ruleIDForTimes.mapValues { $0.reduce(0, +) } + lock.withLock { + ruleIDForTimes.mapValues { $0.reduce(0, +) } + } } /// Register time spent evaluating a rule with the specified ID. @@ -24,10 +26,10 @@ public final class CustomRuleTimer { /// - parameter time: The time interval spent evaluating this rule ID. /// - parameter ruleID: The ID of the rule that was evaluated. func register(time: TimeInterval, forRuleID ruleID: String) { - guard shouldRecord else { return } - - lock.lock() - defer { lock.unlock() } - ruleIDForTimes[ruleID, default: []].append(time) + if shouldRecord { + lock.withLock { + ruleIDForTimes[ruleID, default: []].append(time) + } + } } } diff --git a/Source/SwiftLintFramework/Models/Version.swift b/Source/SwiftLintFramework/Models/Version.swift index 9d10c86ea1..8b29116a70 100644 --- a/Source/SwiftLintFramework/Models/Version.swift +++ b/Source/SwiftLintFramework/Models/Version.swift @@ -1,5 +1,5 @@ /// A type describing the SwiftLint version. -public struct Version: VersionComparable { +public struct Version: VersionComparable, Sendable { /// The string value for this version. public let value: String diff --git a/Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift b/Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift index fab3ef0b5a..418973e6bb 100644 --- a/Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift +++ b/Source/SwiftLintFramework/Rules/SuperfluousDisableCommandRule.swift @@ -1,4 +1,4 @@ -package struct SuperfluousDisableCommandRule: SourceKitFreeRule { +package struct SuperfluousDisableCommandRule: SourceKitFreeRule, Sendable { package var configuration = SeverityConfiguration(.warning) package init() { /* Make initializer as accessible as its type. */ } diff --git a/Tests/SwiftLintTestHelpers/TestHelpers.swift b/Tests/SwiftLintTestHelpers/TestHelpers.swift index f729cadda9..9c4551d5d8 100644 --- a/Tests/SwiftLintTestHelpers/TestHelpers.swift +++ b/Tests/SwiftLintTestHelpers/TestHelpers.swift @@ -9,19 +9,14 @@ private let violationMarker = "↓" private extension SwiftLintFile { static func testFile(withContents contents: String, persistToDisk: Bool = false) -> SwiftLintFile { - let file: SwiftLintFile if persistToDisk { let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent(UUID().uuidString) .appendingPathExtension("swift") _ = try? contents.data(using: .utf8)!.write(to: url) - file = SwiftLintFile(path: url.path)! - } else { - file = SwiftLintFile(contents: contents) + return SwiftLintFile(path: url.path, isTestFile: true)! } - - file.markAsTestFile() - return file + return SwiftLintFile(contents: contents, isTestFile: true) } func makeCompilerArguments() -> [String] { From cff2cbea473617a5233ec71a0ed44d33083065fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 22 Dec 2024 19:55:47 +0100 Subject: [PATCH 127/260] Enable strict concurrency checking --- BUILD | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD b/BUILD index ef07adeba4..453ce4e04d 100644 --- a/BUILD +++ b/BUILD @@ -73,7 +73,7 @@ swift_library( name = "SwiftLintCore", package_name = "SwiftLint", srcs = glob(["Source/SwiftLintCore/**/*.swift"]), - copts = copts, # TODO: strict_concurrency_copts + copts = copts + strict_concurrency_copts, module_name = "SwiftLintCore", plugins = select({ ":universal_tools_config": [":SwiftLintCoreMacros"], diff --git a/Package.swift b/Package.swift index 934c1f1e1a..2e4cb6dde9 100644 --- a/Package.swift +++ b/Package.swift @@ -87,7 +87,7 @@ let package = Package( .product(name: "Yams", package: "Yams"), "SwiftLintCoreMacros", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .target( name: "SwiftLintBuiltInRules", From deaacf6f692031bbe975598b6ba4bcaec92b45d8 Mon Sep 17 00:00:00 2001 From: Martin Redington Date: Wed, 25 Dec 2024 19:43:27 +0000 Subject: [PATCH 128/260] Fixed PrivateUnitTestRule deprecations (#5912) --- CHANGELOG.md | 4 ++ .../Rules/Lint/PrivateUnitTestRule.swift | 16 +---- .../PrivateUnitTestConfiguration.swift | 59 ------------------- .../UnitTestConfiguration.swift | 1 + .../default_rule_configurations.yml | 1 - 5 files changed, 8 insertions(+), 73 deletions(-) delete mode 100644 Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/PrivateUnitTestConfiguration.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a283f0cfe..da9d280956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ * SwiftLint now requires a Swift 6 or higher compiler to build. [SimplyDanny](https://github.com/SimplyDanny) +* The `private_unit_test` rule's deprecated `regex` configuration option has been removed after 2 years. + [Martin Redington](https://github.com/mildm8nnered) + [#5912](https://github.com/realm/SwiftLint/issues/5912) + #### Experimental * None. diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift index 44d0db3b74..e9dc90514e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift @@ -132,11 +132,11 @@ private extension PrivateUnitTestRule { override var skippableDeclarations: [any DeclSyntaxProtocol.Type] { .all } override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { - !node.isPrivate && node.hasParent(configuredIn: configuration) ? .visitChildren : .skipChildren + !node.isPrivate && node.isXCTestCase(configuration.testParentClasses) ? .visitChildren : .skipChildren } override func visitPost(_ node: ClassDeclSyntax) { - if node.isPrivate, node.hasParent(configuredIn: configuration) { + if node.isPrivate, node.isXCTestCase(configuration.testParentClasses) { violations.append(node.classKeyword.positionAfterSkippingLeadingTrivia) } } @@ -150,7 +150,7 @@ private extension PrivateUnitTestRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: ClassDeclSyntax) -> DeclSyntax { - guard node.isPrivate, node.hasParent(configuredIn: configuration) else { + guard node.isPrivate, node.isXCTestCase(configuration.testParentClasses) else { return super.visit(node) } @@ -189,16 +189,6 @@ private extension PrivateUnitTestRule { } private extension ClassDeclSyntax { - func hasParent(configuredIn config: PrivateUnitTestConfiguration) -> Bool { - inheritanceClause?.inheritedTypes.contains { type in - if let name = type.type.as(IdentifierTypeSyntax.self)?.name.text { - return config.regex.regex.numberOfMatches(in: name, range: name.fullNSRange) > 0 - || config.testParentClasses.contains(name) - } - return false - } ?? false - } - var isPrivate: Bool { resultInPrivateProperty(modifiers: modifiers, attributes: attributes) } diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/PrivateUnitTestConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/PrivateUnitTestConfiguration.swift deleted file mode 100644 index 65b14cdcfe..0000000000 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/PrivateUnitTestConfiguration.swift +++ /dev/null @@ -1,59 +0,0 @@ -import Foundation -import SwiftLintCore - -struct PrivateUnitTestConfiguration: SeverityBasedRuleConfiguration { - typealias Parent = PrivateUnitTestRule - - @ConfigurationElement(key: "severity") - private(set) var severityConfiguration = SeverityConfiguration(.warning) - @ConfigurationElement(key: "test_parent_classes") - private(set) var testParentClasses: Set = ["QuickSpec", "XCTestCase"] - - @ConfigurationElement(key: "regex") - private(set) var regex: RegularExpression = "XCTestCase" - - mutating func apply(configuration: Any) throws { - guard let configurationDict = configuration as? [String: Any] else { - throw Issue.invalidConfiguration(ruleID: Parent.identifier) - } - if let extraTestParentClasses = configurationDict[$testParentClasses.key] as? [String] { - self.testParentClasses.formUnion(extraTestParentClasses) - } - if let regexString = configurationDict[$regex.key] as? String { - // TODO: [01/09/2025] Remove deprecation warning after ~2 years and use `UnitTestConfiguration` - // instead of this configuration. - queuedPrintError( - """ - warning: '\($regex.key)' has been replaced by a list of explicit parent class names. They can be \ - configured in the '\($testParentClasses.key)' option. '\($regex.key)' will be completely removed \ - in a future release. - """ - ) - regex = try RegularExpression(pattern: regexString) - } - if configurationDict["included"] is String { - // TODO: [01/09/2025] Remove deprecation warning after ~2 years and replace this configuration by - // `UnitTestConfiguration`. - queuedPrintError( - "warning: 'included' is ignored from now on. You may remove it from the configuration file." - ) - } - if configurationDict["name"] is String { - // TODO: [01/09/2025] Remove deprecation warning after ~2 years and replace this configuration by - // `UnitTestConfiguration`. - queuedPrintError( - "warning: 'name' is ignored from now on. You may remove it from the configuration file." - ) - } - if configurationDict["message"] is String { - // TODO: [01/09/2025] Remove deprecation warning after ~2 years and replace this configuration by - // `UnitTestConfiguration`. - queuedPrintError( - "warning: 'message' is ignored from now on. You may remove it from the configuration file." - ) - } - if let severityString = configurationDict[$severityConfiguration.key] as? String { - try severityConfiguration.apply(configuration: severityString) - } - } -} diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnitTestConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnitTestConfiguration.swift index 96f5d0de6d..e51e3d4101 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnitTestConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnitTestConfiguration.swift @@ -5,6 +5,7 @@ typealias EmptyXCTestMethodConfiguration = UnitTestConfiguration typealias NoMagicNumbersConfiguration = UnitTestConfiguration typealias SingleTestClassConfiguration = UnitTestConfiguration +typealias PrivateUnitTestConfiguration = UnitTestConfiguration @AutoConfigParser struct UnitTestConfiguration: SeverityBasedRuleConfiguration { diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 3a35616033..20f1f83e83 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -437,7 +437,6 @@ private_swiftui_state: private_unit_test: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] - regex: "XCTestCase" prohibited_interface_builder: severity: warning prohibited_super_call: From 03f8c83d0dd19e61326675e27b284927131f37aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 25 Dec 2024 23:05:02 +0100 Subject: [PATCH 129/260] Add new `redundant_sendable` rule (#5902) --- CHANGELOG.md | 5 + .../Models/BuiltInRules.swift | 1 + .../Rules/Lint/RedundantSendableRule.swift | 130 ++++++++++++++++++ .../RedundantSendableConfiguration.swift | 11 ++ Tests/GeneratedTests/GeneratedTests.swift | 6 + .../default_rule_configurations.yml | 3 + 6 files changed, 156 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift create mode 100644 Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantSendableConfiguration.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index da9d280956..4bc4d82647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,11 @@ [dk-talks](https://github.com/dk-talks) [SimplyDanny](https://github.com/SimplyDanny) +* Add new `redundant_sendable` rule that triggers on `Sendable` conformances of + types that are implicitly already `Sendable` due to being actor-isolated. It + is enabled by default. + [SimplyDanny](https://github.com/SimplyDanny) + #### Bug Fixes * Ignore super calls with trailing closures in `unneeded_override` rule. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index b93b94e752..c0403acbaa 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -176,6 +176,7 @@ public let builtInRules: [any Rule.Type] = [ RedundantObjcAttributeRule.self, RedundantOptionalInitializationRule.self, RedundantSelfInClosureRule.self, + RedundantSendableRule.self, RedundantSetAccessControlRule.self, RedundantStringEnumValueRule.self, RedundantTypeAnnotationRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift new file mode 100644 index 0000000000..d29cdc54eb --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift @@ -0,0 +1,130 @@ +import SwiftSyntax + +@SwiftSyntaxRule(explicitRewriter: true) +struct RedundantSendableRule: Rule { + var configuration = RedundantSendableConfiguration() + + static let description = RuleDescription( + identifier: "redundant_sendable", + name: "Redundant Sendable", + description: "Sendable conformance is redundant on an actor-isolated type", + kind: .lint, + nonTriggeringExamples: [ + Example("struct S: Sendable {}"), + Example("class C: Sendable {}"), + Example("actor A {}"), + Example("@MainActor struct S {}"), + Example("@MyActor enum E: Sendable { case a }"), + ], + triggeringExamples: [ + Example("@MainActor struct ↓S: Sendable {}"), + Example("actor ↓A: Sendable {}"), + Example("@MyActor enum ↓E: Sendable { case a }", configuration: ["global_actors": ["MyActor"]]), + ], + corrections: [ + Example("@MainActor struct S: Sendable {}"): + Example("@MainActor struct S {}"), + Example("actor A: Sendable {}"): + Example("actor A {}"), + Example("@MyActor enum E: Sendable { case a }", configuration: ["global_actors": ["MyActor"]]): + Example("@MyActor enum E { case a }"), + Example("actor A: B, Sendable, C {}"): + Example("actor A: B, C {}"), + ] + ) +} + +private extension RedundantSendableRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: ActorDeclSyntax) { + if node.conformsToSendable { + violations.append(at: node.name.positionAfterSkippingLeadingTrivia) + } + } + + override func visitPost(_ node: ClassDeclSyntax) { + collectViolations(in: node) + } + + override func visitPost(_ node: EnumDeclSyntax) { + collectViolations(in: node) + } + + override func visitPost(_ node: ProtocolDeclSyntax) { + collectViolations(in: node) + } + + override func visitPost(_ node: StructDeclSyntax) { + collectViolations(in: node) + } + + private func collectViolations(in decl: some DeclGroupSyntax & NamedDeclSyntax) { + if decl.conformsToSendable, decl.isIsolatedToActor(actors: configuration.globalActors) { + violations.append(at: decl.name.positionAfterSkippingLeadingTrivia) + } + } + } + + final class Rewriter: ViolationsSyntaxRewriter { + override func visit(_ node: ActorDeclSyntax) -> DeclSyntax { + if node.conformsToSendable { + correctionPositions.append(node.name.positionAfterSkippingLeadingTrivia) + return super.visit(node.withoutSendable) + } + return super.visit(node) + } + + override func visit(_ node: ClassDeclSyntax) -> DeclSyntax { + super.visit(removeRedundantSendable(from: node)) + } + + override func visit(_ node: EnumDeclSyntax) -> DeclSyntax { + super.visit(removeRedundantSendable(from: node)) + } + + override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax { + super.visit(removeRedundantSendable(from: node)) + } + + override func visit(_ node: StructDeclSyntax) -> DeclSyntax { + super.visit(removeRedundantSendable(from: node)) + } + + private func removeRedundantSendable(from decl: T) -> T { + if decl.conformsToSendable, decl.isIsolatedToActor(actors: configuration.globalActors) { + correctionPositions.append(decl.name.positionAfterSkippingLeadingTrivia) + return decl.withoutSendable + } + return decl + } + } +} + +private extension DeclGroupSyntax where Self: NamedDeclSyntax { + var conformsToSendable: Bool { + inheritanceClause?.inheritedTypes.contains(where: \.isSendable) == true + } + + func isIsolatedToActor(actors: Set) -> Bool { + attributes.contains(attributeNamed: "MainActor") || actors.contains { attributes.contains(attributeNamed: $0) } + } + + var withoutSendable: Self { + guard let inheritanceClause else { + return self + } + let inheritedTypes = inheritanceClause.inheritedTypes.filter { !$0.isSendable } + if inheritedTypes.isEmpty { + return with(\.inheritanceClause, nil) + .with(\.name.trailingTrivia, inheritanceClause.leadingTrivia + inheritanceClause.trailingTrivia) + } + return with(\.inheritanceClause, inheritanceClause + .with(\.inheritedTypes, inheritedTypes)) + } +} + +private extension InheritedTypeSyntax { + var isSendable: Bool { + type.as(IdentifierTypeSyntax.self)?.name.text == "Sendable" + } +} diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantSendableConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantSendableConfiguration.swift new file mode 100644 index 0000000000..4f8bb3b8bd --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantSendableConfiguration.swift @@ -0,0 +1,11 @@ +import SwiftLintCore + +@AutoConfigParser +struct RedundantSendableConfiguration: SeverityBasedRuleConfiguration { + typealias Parent = RedundantSendableRule + + @ConfigurationElement(key: "severity") + private(set) var severityConfiguration = SeverityConfiguration(.warning) + @ConfigurationElement(key: "global_actors") + private(set) var globalActors = Set() +} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index a67ea93b77..6ef660cb4d 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -1045,6 +1045,12 @@ final class RedundantSelfInClosureRuleGeneratedTests: SwiftLintTestCase { } } +final class RedundantSendableRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(RedundantSendableRule.description) + } +} + final class RedundantSetAccessControlRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(RedundantSetAccessControlRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 20f1f83e83..b619f7f0a2 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -467,6 +467,9 @@ redundant_optional_initialization: severity: warning redundant_self_in_closure: severity: warning +redundant_sendable: + severity: warning + global_actors: [] redundant_set_access_control: severity: warning redundant_string_enum_value: From 152355e36f97ef5cf1c420181237f2e89e653b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 25 Dec 2024 23:33:33 +0100 Subject: [PATCH 130/260] Improve performance of excluded files filter (#5157) The current algorithm is like "collect all included files and subtract all excluded files". Collecting all included and all excluded files relies on the file system. This can become slow when the patterns used to exclude files resolve to a large number of files. The new approach only collects all lintable files and checks them against the exclude patterns. This can be done by in-memory string-regex-match and does therefore not require file system accesses. --- BUILD | 1 + CHANGELOG.md | 4 + MODULE.bazel | 1 + Package.resolved | 9 +++ Package.swift | 2 + .../Extensions/String+SwiftLint.swift | 9 +++ .../Configuration+CommandLine.swift | 23 +++--- .../Configuration+LintableFiles.swift | 80 +++++++------------ Source/SwiftLintFramework/Helpers/Glob.swift | 23 ++++++ Tests/IntegrationTests/IntegrationTests.swift | 4 +- .../ConfigurationTests.swift | 37 ++++----- Tests/SwiftLintFrameworkTests/GlobTests.swift | 27 +++++++ bazel/repos.bzl | 7 ++ tools/oss-check | 14 ++-- 14 files changed, 145 insertions(+), 96 deletions(-) diff --git a/BUILD b/BUILD index 453ce4e04d..bf7e15a9e6 100644 --- a/BUILD +++ b/BUILD @@ -89,6 +89,7 @@ swift_library( ":SourceKittenFramework.wrapper", "@sourcekitten_com_github_jpsim_yams//:Yams", "@swiftlint_com_github_scottrhoyt_swifty_text_table//:SwiftyTextTable", + "@com_github_ileitch_swift-filename-matcher//:FilenameMatcher" ] + select({ "@platforms//os:linux": ["@com_github_krzyzanowskim_cryptoswift//:CryptoSwift"], "//conditions:default": [":DyldWarningWorkaround"], diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bc4d82647..b575048de4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,10 @@ is enabled by default. [SimplyDanny](https://github.com/SimplyDanny) +* Improve performance when exclude patterns resolve to a large set of files. + [SimplyDanny](https://github.com/SimplyDanny) + [#5018](https://github.com/realm/SwiftLint/issues/5018) + #### Bug Fixes * Ignore super calls with trailing closures in `unneeded_override` rule. diff --git a/MODULE.bazel b/MODULE.bazel index ff17e604d9..a016dbbe0e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,6 +14,7 @@ bazel_dep(name = "sourcekitten", version = "0.36.0", repo_name = "com_github_jps bazel_dep(name = "swift-syntax", version = "600.0.0", repo_name = "SwiftSyntax") bazel_dep(name = "swift_argument_parser", version = "1.3.1.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser") bazel_dep(name = "yams", version = "5.1.3", repo_name = "sourcekitten_com_github_jpsim_yams") +bazel_dep(name = "swift-filename-matcher", version = "2.0.0", repo_name = "com_github_ileitch_swift-filename-matcher") swiftlint_repos = use_extension("//bazel:repos.bzl", "swiftlint_repos_bzlmod") use_repo( diff --git a/Package.resolved b/Package.resolved index baa9895fe5..b5cb643c48 100644 --- a/Package.resolved +++ b/Package.resolved @@ -36,6 +36,15 @@ "version" : "1.3.1" } }, + { + "identity" : "swift-filename-matcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ileitch/swift-filename-matcher", + "state" : { + "revision" : "516ff95f6a06c7a9eff8e944e989c7af076c5cdb", + "version" : "2.0.0" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 2e4cb6dde9..e7e7ea10b1 100644 --- a/Package.swift +++ b/Package.swift @@ -36,6 +36,7 @@ let package = Package( .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.2.0"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.8.4")), + .package(url: "https://github.com/ileitch/swift-filename-matcher", .upToNextMinor(from: "2.0.0")), ], targets: [ .plugin( @@ -85,6 +86,7 @@ let package = Package( .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), .product(name: "SwiftyTextTable", package: "SwiftyTextTable"), .product(name: "Yams", package: "Yams"), + .product(name: "FilenameMatcher", package: "swift-filename-matcher"), "SwiftLintCoreMacros", ], swiftSettings: swiftFeatures + strictConcurrency diff --git a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift index 49839438b7..73d93adf9f 100644 --- a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift @@ -68,11 +68,20 @@ public extension String { /// Returns a new string, converting the path to a canonical absolute path. /// + /// > Important: This method might use an incorrect working directory internally. This can cause test failures + /// in Bazel builds but does not seem to cause trouble in production. + /// /// - returns: A new `String`. func absolutePathStandardized() -> String { bridge().absolutePathRepresentation().bridge().standardizingPath } + /// Like ``absolutePathStandardized()`` but with the working directory that's used everywhere else. + var normalized: String { + let cwd = FileManager.default.currentDirectoryPath.bridge().standardizingPath + return bridge().absolutePathRepresentation(rootDirectory: cwd) + } + var isFile: Bool { if self.isEmpty { return false diff --git a/Source/SwiftLintFramework/Configuration+CommandLine.swift b/Source/SwiftLintFramework/Configuration+CommandLine.swift index c24f5c9c55..987542a5be 100644 --- a/Source/SwiftLintFramework/Configuration+CommandLine.swift +++ b/Source/SwiftLintFramework/Configuration+CommandLine.swift @@ -257,15 +257,12 @@ extension Configuration { guard options.forceExclude else { return files } - let scriptInputPaths = files.compactMap(\.path) - - if options.useExcludingByPrefix { - return filterExcludedPathsByPrefix(in: scriptInputPaths) - .map(SwiftLintFile.init(pathDeferringReading:)) - } - return filterExcludedPaths(excludedPaths(), in: scriptInputPaths) - .map(SwiftLintFile.init(pathDeferringReading:)) + return ( + visitor.options.useExcludingByPrefix + ? filterExcludedPathsByPrefix(in: scriptInputPaths) + : filterExcludedPaths(in: scriptInputPaths) + ).map(SwiftLintFile.init(pathDeferringReading:)) } if !options.quiet { let filesInfo: String @@ -277,14 +274,12 @@ extension Configuration { queuedPrintError("\(options.capitalizedVerb) Swift files \(filesInfo)") } - let excludeLintableFilesBy = options.useExcludingByPrefix - ? Configuration.ExcludeBy.prefix - : .paths(excludedPaths: excludedPaths()) - return options.paths.flatMap { + return visitor.options.paths.flatMap { self.lintableFiles( inPath: $0, - forceExclude: options.forceExclude, - excludeBy: excludeLintableFilesBy) + forceExclude: visitor.options.forceExclude, + excludeByPrefix: visitor.options.useExcludingByPrefix + ) } } diff --git a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift index 30ea7be975..67dc6d00ce 100644 --- a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift @@ -1,25 +1,21 @@ import Foundation extension Configuration { - public enum ExcludeBy { - case prefix - case paths(excludedPaths: [String]) - } - // MARK: Lintable Paths + /// Returns the files that can be linted by SwiftLint in the specified parent path. /// /// - parameter path: The parent path in which to search for lintable files. Can be a directory or a /// file. /// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if /// `path` is an exact match. - /// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorithm. + /// - parameter excludeByPrefix: Whether or not it uses the exclude-by-prefix algorithm. /// /// - returns: Files to lint. public func lintableFiles(inPath path: String, forceExclude: Bool, - excludeBy: ExcludeBy) -> [SwiftLintFile] { - lintablePaths(inPath: path, forceExclude: forceExclude, excludeBy: excludeBy) + excludeByPrefix: Bool) -> [SwiftLintFile] { + lintablePaths(inPath: path, forceExclude: forceExclude, excludeByPrefix: excludeByPrefix) .compactMap(SwiftLintFile.init(pathDeferringReading:)) } @@ -29,24 +25,21 @@ extension Configuration { /// file. /// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if /// `path` is an exact match. - /// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorithm. + /// - parameter excludeByPrefix: Whether or not it uses the exclude-by-prefix algorithm. /// - parameter fileManager: The lintable file manager to use to search for lintable files. /// /// - returns: Paths for files to lint. internal func lintablePaths( inPath path: String, forceExclude: Bool, - excludeBy: ExcludeBy, + excludeByPrefix: Bool, fileManager: some LintableFileManager = FileManager.default ) -> [String] { if fileManager.isFile(atPath: path) { if forceExclude { - switch excludeBy { - case .prefix: - return filterExcludedPathsByPrefix(in: [path.absolutePathStandardized()]) - case .paths(let excludedPaths): - return filterExcludedPaths(excludedPaths, in: [path.absolutePathStandardized()]) - } + return excludeByPrefix + ? filterExcludedPathsByPrefix(in: [path.normalized]) + : filterExcludedPaths(in: [path.normalized]) } // If path is a file and we're not forcing excludes, skip filtering with excluded/included paths return [path] @@ -57,35 +50,29 @@ extension Configuration { .flatMap(Glob.resolveGlob) .parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) } - switch excludeBy { - case .prefix: - return filterExcludedPathsByPrefix(in: pathsForPath, includedPaths) - case .paths(let excludedPaths): - return filterExcludedPaths(excludedPaths, in: pathsForPath, includedPaths) - } + return excludeByPrefix + ? filterExcludedPathsByPrefix(in: pathsForPath + includedPaths) + : filterExcludedPaths(in: pathsForPath + includedPaths) } /// Returns an array of file paths after removing the excluded paths as defined by this configuration. /// - /// - parameter fileManager: The lintable file manager to use to expand the excluded paths into all matching paths. - /// - parameter paths: The input paths to filter. + /// - parameter paths: The input paths to filter. /// /// - returns: The input paths after removing the excluded paths. - public func filterExcludedPaths( - _ excludedPaths: [String], - in paths: [String]... - ) -> [String] { - let allPaths = paths.flatMap { $0 } + public func filterExcludedPaths(in paths: [String]) -> [String] { #if os(Linux) - let result = NSMutableOrderedSet(capacity: allPaths.count) - result.addObjects(from: allPaths) + let result = NSMutableOrderedSet(capacity: paths.count) + result.addObjects(from: paths) #else - let result = NSMutableOrderedSet(array: allPaths) + let result = NSMutableOrderedSet(array: paths) #endif - - result.minusSet(Set(excludedPaths)) - // swiftlint:disable:next force_cast - return result.map { $0 as! String } + let exclusionPatterns = self.excludedPaths.flatMap { + Glob.createFilenameMatchers(root: rootDirectory, pattern: $0) + } + return result.array + .parallelCompactMap { exclusionPatterns.anyMatch(filename: $0 as! String) ? nil : $0 as? String } + // swiftlint:disable:previous force_cast } /// Returns the file paths that are excluded by this configuration using filtering by absolute path prefix. @@ -94,25 +81,12 @@ extension Configuration { /// algorithm `filterExcludedPaths`. /// /// - returns: The input paths after removing the excluded paths. - public func filterExcludedPathsByPrefix(in paths: [String]...) -> [String] { - let allPaths = paths.flatMap { $0 } + public func filterExcludedPathsByPrefix(in paths: [String]) -> [String] { let excludedPaths = self.excludedPaths - .parallelFlatMap { @Sendable in Glob.resolveGlob($0) } - .map { $0.absolutePathStandardized() } - return allPaths.filter { path in + .parallelFlatMap { Glob.resolveGlob($0) } + .map(\.normalized) + return paths.filter { path in !excludedPaths.contains { path.hasPrefix($0) } } } - - /// Returns the file paths that are excluded by this configuration after expanding them using the specified file - /// manager. - /// - /// - parameter fileManager: The file manager to get child paths in a given parent location. - /// - /// - returns: The expanded excluded file paths. - public func excludedPaths(fileManager: some LintableFileManager = FileManager.default) -> [String] { - excludedPaths - .flatMap(Glob.resolveGlob) - .parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) } - } } diff --git a/Source/SwiftLintFramework/Helpers/Glob.swift b/Source/SwiftLintFramework/Helpers/Glob.swift index 4ef3cfbacf..9ef79f1ca0 100644 --- a/Source/SwiftLintFramework/Helpers/Glob.swift +++ b/Source/SwiftLintFramework/Helpers/Glob.swift @@ -1,3 +1,4 @@ +import FilenameMatcher import Foundation #if os(Linux) @@ -36,6 +37,28 @@ struct Glob { .map { $0.absolutePathStandardized() } } + static func createFilenameMatchers(root: String, pattern: String) -> [FilenameMatcher] { + var absolutPathPattern = pattern + if !pattern.starts(with: root) { + // If the root is not already part of the pattern, prepend it. + absolutPathPattern = root + (root.hasSuffix("/") ? "" : "/") + absolutPathPattern + } + if pattern.hasSuffix(".swift") || pattern.hasSuffix("/**") { + // Suffix is already well defined. + return [FilenameMatcher(pattern: absolutPathPattern)] + } + if pattern.hasSuffix("/") { + // Matching all files in the folder. + return [FilenameMatcher(pattern: absolutPathPattern + "**")] + } + // The pattern could match files in the last folder in the path or all contained files if the last component + // represents folders. + return [ + FilenameMatcher(pattern: absolutPathPattern), + FilenameMatcher(pattern: absolutPathPattern + "/**"), + ] + } + // MARK: Private private static func expandGlobstar(pattern: String) -> [String] { diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index c4830a3ed1..fcf3e561db 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -19,7 +19,7 @@ final class IntegrationTests: SwiftLintTestCase { let swiftFiles = config.lintableFiles( inPath: "", forceExclude: false, - excludeBy: .paths(excludedPaths: config.excludedPaths())) + excludeByPrefix: false) XCTAssert( swiftFiles.contains(where: { #filePath.bridge().absolutePathRepresentation() == $0.path }), "current file should be included" @@ -40,7 +40,7 @@ final class IntegrationTests: SwiftLintTestCase { let swiftFiles = config.lintableFiles( inPath: "", forceExclude: false, - excludeBy: .paths(excludedPaths: config.excludedPaths())) + excludeByPrefix: false) let storage = RuleStorage() let corrections = swiftFiles.parallelFlatMap { Linter(file: $0, configuration: config).collect(into: storage).correct(using: storage) diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift index f88852c94c..2fa222e1a2 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift @@ -288,10 +288,9 @@ final class ConfigurationTests: SwiftLintTestCase { excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"] ) - let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "", forceExclude: false, - excludeBy: .paths(excludedPaths: excludedPaths), + excludeByPrefix: false, fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -299,10 +298,9 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesFile() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/ExcludedFile.swift"]) - let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory/ExcludedFile.swift", forceExclude: true, - excludeBy: .paths(excludedPaths: excludedPaths), + excludeByPrefix: false, fileManager: fileManager) XCTAssertEqual([], paths) } @@ -311,10 +309,9 @@ final class ConfigurationTests: SwiftLintTestCase { let fileManager = TestFileManager() let configuration = Configuration(includedPaths: ["directory"], excludedPaths: ["directory/ExcludedFile.swift", "directory/excluded"]) - let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "", forceExclude: true, - excludeBy: .paths(excludedPaths: excludedPaths), + excludeByPrefix: false, fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -322,10 +319,9 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesDirectory() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"]) - let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true, - excludeBy: .paths(excludedPaths: excludedPaths), + excludeByPrefix: false, fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -333,19 +329,17 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"]) - let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true, - excludeBy: .paths(excludedPaths: excludedPaths), + excludeByPrefix: false, fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } func testLintablePaths() { - let excluded = Configuration.default.excludedPaths(fileManager: TestFileManager()) let paths = Configuration.default.lintablePaths(inPath: Mock.Dir.level0, forceExclude: false, - excludeBy: .paths(excludedPaths: excluded)) + excludeByPrefix: false) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() let expectedFilenames = [ "DirectoryLevel1.swift", @@ -361,7 +355,7 @@ final class ConfigurationTests: SwiftLintTestCase { let configuration = Configuration(includedPaths: ["**/Level2"]) let paths = configuration.lintablePaths(inPath: Mock.Dir.level0, forceExclude: true, - excludeBy: .paths(excludedPaths: configuration.excludedPaths)) + excludeByPrefix: false) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() let expectedFilenames = ["Level2.swift", "Level3.swift"] @@ -374,10 +368,9 @@ final class ConfigurationTests: SwiftLintTestCase { excludedPaths: [Mock.Dir.level3.stringByAppendingPathComponent("*.swift")] ) - let excludedPaths = configuration.excludedPaths() let lintablePaths = configuration.lintablePaths(inPath: "", forceExclude: false, - excludeBy: .paths(excludedPaths: excludedPaths)) + excludeByPrefix: false) XCTAssertEqual(lintablePaths, []) } @@ -492,7 +485,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: Mock.Dir.level0, forceExclude: false, - excludeBy: .prefix) + excludeByPrefix: true) let filenames = paths.map { $0.bridge().lastPathComponent } XCTAssertEqual(filenames, ["Level2.swift"]) } @@ -502,7 +495,7 @@ extension ConfigurationTests { let configuration = Configuration(excludedPaths: ["Level1/Level2/Level3/Level3.swift"]) let paths = configuration.lintablePaths(inPath: "Level1/Level2/Level3/Level3.swift", forceExclude: true, - excludeBy: .prefix) + excludeByPrefix: true) XCTAssertEqual([], paths) } @@ -512,7 +505,7 @@ extension ConfigurationTests { excludedPaths: ["Level1/Level1.swift"]) let paths = configuration.lintablePaths(inPath: "Level1", forceExclude: true, - excludeBy: .prefix) + excludeByPrefix: true) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(["Level2.swift", "Level3.swift"], filenames) } @@ -526,7 +519,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: ".", forceExclude: true, - excludeBy: .prefix) + excludeByPrefix: true) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(["Level0.swift", "Level1.swift"], filenames) } @@ -540,7 +533,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: ".", forceExclude: true, - excludeBy: .prefix) + excludeByPrefix: true) let filenames = paths.map { $0.bridge().lastPathComponent } XCTAssertEqual(["Level0.swift"], filenames) } @@ -552,7 +545,7 @@ extension ConfigurationTests { excludedPaths: ["Level1/*/*.swift", "Level1/*/*/*.swift"]) let paths = configuration.lintablePaths(inPath: "Level1", forceExclude: false, - excludeBy: .prefix) + excludeByPrefix: true) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(filenames, ["Level1.swift"]) } @@ -606,7 +599,7 @@ extension ConfigurationTests { private extension Sequence where Element == String { func absolutePathsStandardized() -> [String] { - map { $0.absolutePathStandardized() } + map(\.normalized) } } diff --git a/Tests/SwiftLintFrameworkTests/GlobTests.swift b/Tests/SwiftLintFrameworkTests/GlobTests.swift index 04730dc09a..81b53677b2 100644 --- a/Tests/SwiftLintFrameworkTests/GlobTests.swift +++ b/Tests/SwiftLintFrameworkTests/GlobTests.swift @@ -82,4 +82,31 @@ final class GlobTests: SwiftLintTestCase { let files = Glob.resolveGlob(mockPath.stringByAppendingPathComponent("**/*.swift")) XCTAssertEqual(files.sorted(), expectedFiles.sorted()) } + + func testCreateFilenameMatchers() { + func assertGlobMatch(root: String, pattern: String, filename: String) { + let matchers = Glob.createFilenameMatchers(root: root, pattern: pattern) + XCTAssert(matchers.anyMatch(filename: filename)) + } + + assertGlobMatch(root: "/a/b/", pattern: "c/*.swift", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "/a", pattern: "**/*.swift", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "/a", pattern: "**/*.swift", filename: "/a/b.swift") + assertGlobMatch(root: "", pattern: "**/*.swift", filename: "/a/b.swift") + assertGlobMatch(root: "", pattern: "a/**/b.swift", filename: "a/b.swift") + assertGlobMatch(root: "", pattern: "a/**/b.swift", filename: "a/c/b.swift") + assertGlobMatch(root: "", pattern: "**/*.swift", filename: "a.swift") + assertGlobMatch(root: "", pattern: "a/**/*.swift", filename: "a/b/c.swift") + assertGlobMatch(root: "", pattern: "a/**/*.swift", filename: "a/b.swift") + assertGlobMatch(root: "/a/b", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "/a/", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") + + assertGlobMatch(root: "", pattern: "/a/b/c", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "", pattern: "/a/b/c/", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "", pattern: "/d.swift/*.swift", filename: "/d.swift/e.swift") + assertGlobMatch(root: "", pattern: "/a/**", filename: "/a/b/c/d.swift") + assertGlobMatch(root: "", pattern: "**/*Test*", filename: "/a/b/c/MyTest2.swift") + assertGlobMatch(root: "", pattern: "**/*Test*", filename: "/a/b/MyTests/c.swift") + } } diff --git a/bazel/repos.bzl b/bazel/repos.bzl index 47f2aa9d39..53d99188ea 100644 --- a/bazel/repos.bzl +++ b/bazel/repos.bzl @@ -64,6 +64,13 @@ def swiftlint_repos(bzlmod = False): url = "https://github.com/krzyzanowskim/CryptoSwift/archive/refs/tags/1.8.4.tar.gz", ) + http_archive( + name = "com_github_ileitch_swift-filename-matcher", + sha256 = "1adbb1eb042910f996689827f7dee217bebf7c5178f34178bcfe468b5b3268a2", + strip_prefix = "swift-filename-matcher-2.0.0", + url = "https://github.com/ileitch/swift-filename-matcher/archive/refs/tags/2.0.0.tar.gz", + ) + def _swiftlint_repos_bzlmod(_): swiftlint_repos(bzlmod = True) diff --git a/tools/oss-check b/tools/oss-check index 77a70c8365..f1d0a05ca8 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -45,6 +45,7 @@ end.parse! class Repo attr_accessor :name attr_accessor :github_location + attr_accessor :keep_config attr_accessor :config attr_accessor :commit_hash attr_accessor :branch_exit_value @@ -52,9 +53,10 @@ class Repo attr_accessor :main_exit_value attr_accessor :main_duration - def initialize(name, github_location, config=nil) + def initialize(name, github_location, keep_config=false, config=nil) @name = name @github_location = github_location + @keep_config = keep_config @config = config end @@ -140,7 +142,9 @@ def setup_repos puts "Cloning #{repo}" perform("git clone #{repo.git_url} --depth 1 #{dir} 2> /dev/null") swiftlint_config = "#{dir}/.swiftlint.yml" - FileUtils.rm_rf(swiftlint_config) + if !repo.keep_config + FileUtils.rm_rf(swiftlint_config) + end if repo.config File.open(swiftlint_config, 'a') do |file| file.puts(repo.config) @@ -292,7 +296,7 @@ end @repos = [ Repo.new('Aerial', 'JohnCoates/Aerial'), Repo.new('Alamofire', 'Alamofire/Alamofire'), - Repo.new('Brave', 'brave/brave-ios'), + Repo.new('Brave', 'brave/brave-ios', true), Repo.new('DuckDuckGo', 'duckduckgo/iOS'), Repo.new('Firefox', 'mozilla-mobile/firefox-ios'), Repo.new('Kickstarter', 'kickstarter/ios-oss'), @@ -303,9 +307,9 @@ end Repo.new('Quick', 'Quick/Quick'), Repo.new('Realm', 'realm/realm-swift'), Repo.new('Sourcery', 'krzysztofzablocki/Sourcery'), - Repo.new('Swift', 'apple/swift', 'included: stdlib'), + Repo.new('Swift', 'apple/swift', false, 'included: stdlib'), Repo.new('VLC', 'videolan/vlc-ios'), - Repo.new('Wire', 'wireapp/wire-ios', 'excluded: wire-ios/Templates/Viper'), + Repo.new('Wire', 'wireapp/wire-ios', false, 'excluded: wire-ios/Templates/Viper'), Repo.new('WordPress', 'wordpress-mobile/WordPress-iOS') ] From 317eb4382fe040e9af2133316ad3696765724154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 26 Dec 2024 12:07:53 +0100 Subject: [PATCH 131/260] Test on default or opt-in rule (#5913) --- Tests/IntegrationTests/IntegrationTests.swift | 13 +- .../default_rule_configurations.yml | 486 ++++++++++++++++++ 2 files changed, 494 insertions(+), 5 deletions(-) diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index fcf3e561db..54e12ec8fc 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -57,11 +57,14 @@ final class IntegrationTests: SwiftLintTestCase { let defaultConfig = Configuration(rulesMode: .allCommandLine).rules .map { type(of: $0) } .filter { $0.identifier != "custom_rules" } - .map { - """ - \($0.identifier): - \($0.init().createConfigurationDescription().yaml().indent(by: 2)) - """ + .map { ruleType in + let rule = ruleType.init() + return """ + \(ruleType.identifier): + \(rule.createConfigurationDescription().yaml().indent(by: 2)) + meta: + opt-in: \(rule is any OptInRule) + """ } .joined(separator: "\n") let referenceFile = URL(fileURLWithPath: #filePath) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index b619f7f0a2..717d6aec93 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -1,83 +1,149 @@ accessibility_label_for_image: severity: warning + meta: + opt-in: true accessibility_trait_for_button: severity: warning + meta: + opt-in: true anonymous_argument_in_multiline_closure: severity: warning + meta: + opt-in: true array_init: severity: warning + meta: + opt-in: true async_without_await: severity: warning + meta: + opt-in: true attribute_name_spacing: severity: error + meta: + opt-in: false attributes: severity: warning attributes_with_arguments_always_on_line_above: true always_on_same_line: ["@IBAction", "@NSManaged"] always_on_line_above: [] + meta: + opt-in: true balanced_xctest_lifecycle: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true blanket_disable_command: severity: warning allowed_rules: ["file_header", "file_length", "file_name", "file_name_no_space", "single_test_class"] always_blanket_disable: [] + meta: + opt-in: false block_based_kvo: severity: warning + meta: + opt-in: false capture_variable: severity: warning + meta: + opt-in: true class_delegate_protocol: severity: warning + meta: + opt-in: false closing_brace: severity: warning + meta: + opt-in: false closure_body_length: warning: 30 error: 100 + meta: + opt-in: true closure_end_indentation: severity: warning + meta: + opt-in: true closure_parameter_position: severity: warning + meta: + opt-in: false closure_spacing: severity: warning + meta: + opt-in: true collection_alignment: severity: warning align_colons: false + meta: + opt-in: true colon: severity: warning flexible_right_spacing: false apply_to_dictionaries: true + meta: + opt-in: false comma: severity: warning + meta: + opt-in: false comma_inheritance: severity: warning + meta: + opt-in: true comment_spacing: severity: warning + meta: + opt-in: false compiler_protocol_init: severity: warning + meta: + opt-in: false computed_accessors_order: severity: warning order: get_set + meta: + opt-in: false conditional_returns_on_newline: severity: warning if_only: false + meta: + opt-in: true contains_over_filter_count: severity: warning + meta: + opt-in: true contains_over_filter_is_empty: severity: warning + meta: + opt-in: true contains_over_first_not_nil: severity: warning + meta: + opt-in: true contains_over_range_nil_comparison: severity: warning + meta: + opt-in: true contrasted_opening_brace: severity: warning + meta: + opt-in: true control_statement: severity: warning + meta: + opt-in: false convenience_type: severity: warning + meta: + opt-in: true cyclomatic_complexity: warning: 10 error: 20 ignores_case_statements: false + meta: + opt-in: false deployment_target: severity: warning iOSApplicationExtension_deployment_target: 7.0 @@ -88,54 +154,98 @@ deployment_target: tvOS_deployment_target: 9.0 watchOSApplicationExtension_deployment_target: 1.0 watchOS_deployment_target: 1.0 + meta: + opt-in: false direct_return: severity: warning + meta: + opt-in: true discarded_notification_center_observer: severity: warning + meta: + opt-in: true discouraged_assert: severity: warning + meta: + opt-in: true discouraged_direct_init: severity: warning types: ["Bundle", "Bundle.init", "NSError", "NSError.init", "UIDevice", "UIDevice.init"] + meta: + opt-in: false discouraged_none_name: severity: warning + meta: + opt-in: true discouraged_object_literal: severity: warning image_literal: true color_literal: true + meta: + opt-in: true discouraged_optional_boolean: severity: warning + meta: + opt-in: true discouraged_optional_collection: severity: warning + meta: + opt-in: true duplicate_conditions: severity: error + meta: + opt-in: false duplicate_enum_cases: severity: error + meta: + opt-in: false duplicate_imports: severity: warning + meta: + opt-in: false duplicated_key_in_dictionary_literal: severity: warning + meta: + opt-in: false dynamic_inline: severity: error + meta: + opt-in: false empty_collection_literal: severity: warning + meta: + opt-in: true empty_count: severity: error only_after_dot: false + meta: + opt-in: true empty_enum_arguments: severity: warning + meta: + opt-in: false empty_parameters: severity: warning + meta: + opt-in: false empty_parentheses_with_trailing_closure: severity: warning + meta: + opt-in: false empty_string: severity: warning + meta: + opt-in: true empty_xctest_method: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true enum_case_associated_values_count: warning: 5 error: 6 + meta: + opt-in: true expiring_todo: approaching_expiry_severity: warning expired_severity: error @@ -146,33 +256,57 @@ expiring_todo: closing: "]" date_format: "MM/dd/yyyy" date_separator: "/" + meta: + opt-in: true explicit_acl: severity: warning + meta: + opt-in: true explicit_enum_raw_value: severity: warning + meta: + opt-in: true explicit_init: severity: warning include_bare_init: false + meta: + opt-in: true explicit_self: severity: warning + meta: + opt-in: true explicit_top_level_acl: severity: warning + meta: + opt-in: true explicit_type_interface: severity: warning excluded: [] allow_redundancy: false + meta: + opt-in: true extension_access_modifier: severity: warning + meta: + opt-in: true fallthrough: severity: warning + meta: + opt-in: true fatal_error_message: severity: warning + meta: + opt-in: true file_header: severity: warning + meta: + opt-in: true file_length: warning: 400 error: 1000 ignore_comment_only_lines: false + meta: + opt-in: false file_name: severity: warning excluded: ["LinuxMain.swift", "main.swift"] @@ -180,38 +314,64 @@ file_name: suffix_pattern: "\+.*" nested_type_separator: "." require_fully_qualified_names: false + meta: + opt-in: true file_name_no_space: severity: warning excluded: [] + meta: + opt-in: true file_types_order: severity: warning order: [[supporting_type], [main_type], [extension], [preview_provider], [library_content_provider]] + meta: + opt-in: true final_test_case: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true first_where: severity: warning + meta: + opt-in: true flatmap_over_map_reduce: severity: warning + meta: + opt-in: true for_where: severity: warning allow_for_as_filter: false + meta: + opt-in: false force_cast: severity: error + meta: + opt-in: false force_try: severity: error + meta: + opt-in: false force_unwrapping: severity: warning + meta: + opt-in: true function_body_length: warning: 50 error: 100 + meta: + opt-in: false function_default_parameter_at_end: severity: warning ignore_first_isolation_inheritance_parameter: true + meta: + opt-in: true function_parameter_count: warning: 5 error: 8 ignores_default_parameters: true + meta: + opt-in: false generic_type_name: min_length: warning: 1 @@ -223,10 +383,16 @@ generic_type_name: allowed_symbols: [] unallowed_symbols_severity: error validates_start_with_lowercase: error + meta: + opt-in: false ibinspectable_in_extension: severity: warning + meta: + opt-in: true identical_operands: severity: warning + meta: + opt-in: true identifier_name: min_length: warning: 3 @@ -239,53 +405,95 @@ identifier_name: unallowed_symbols_severity: error validates_start_with_lowercase: error additional_operators: ["!", "%", "&", "*", "+", "-", ".", "/", "<", "=", ">", "?", "^", "|", "~"] + meta: + opt-in: false implicit_getter: severity: warning + meta: + opt-in: false implicit_return: severity: warning included: [closure, function, getter, initializer, subscript] + meta: + opt-in: true implicitly_unwrapped_optional: severity: warning mode: all_except_iboutlets + meta: + opt-in: true inclusive_language: severity: warning + meta: + opt-in: false indentation_width: severity: warning indentation_width: 4 include_comments: true include_compiler_directives: true include_multiline_strings: true + meta: + opt-in: true invalid_swiftlint_command: severity: warning + meta: + opt-in: false is_disjoint: severity: warning + meta: + opt-in: false joined_default_parameter: severity: warning + meta: + opt-in: true large_tuple: warning: 2 error: 3 + meta: + opt-in: false last_where: severity: warning + meta: + opt-in: true leading_whitespace: severity: warning + meta: + opt-in: false legacy_cggeometry_functions: severity: warning + meta: + opt-in: false legacy_constant: severity: warning + meta: + opt-in: false legacy_constructor: severity: warning + meta: + opt-in: false legacy_hashing: severity: warning + meta: + opt-in: false legacy_multiple: severity: warning + meta: + opt-in: true legacy_nsgeometry_functions: severity: warning + meta: + opt-in: false legacy_objc_type: severity: warning + meta: + opt-in: true legacy_random: severity: warning + meta: + opt-in: false let_var_whitespace: severity: warning + meta: + opt-in: true line_length: warning: 120 error: 200 @@ -294,40 +502,68 @@ line_length: ignores_comments: false ignores_interpolated_strings: false excluded_lines_patterns: [] + meta: + opt-in: false literal_expression_end_indentation: severity: warning + meta: + opt-in: true local_doc_comment: severity: warning + meta: + opt-in: true lower_acl_than_parent: severity: warning + meta: + opt-in: true mark: severity: warning + meta: + opt-in: false missing_docs: warning: [open, public] excludes_extensions: true excludes_inherited_types: true excludes_trivial_init: false evaluate_effective_access_control_level: false + meta: + opt-in: true modifier_order: severity: warning preferred_modifier_order: [override, acl, setterACL, dynamic, mutators, lazy, final, required, convenience, typeMethods, owned] + meta: + opt-in: true multiline_arguments: severity: warning first_argument_location: any_line only_enforce_after_first_closure_on_first_line: false + meta: + opt-in: true multiline_arguments_brackets: severity: warning + meta: + opt-in: true multiline_function_chains: severity: warning + meta: + opt-in: true multiline_literal_brackets: severity: warning + meta: + opt-in: true multiline_parameters: severity: warning allows_single_line: true + meta: + opt-in: true multiline_parameters_brackets: severity: warning + meta: + opt-in: true multiple_closures_with_trailing_closure: severity: warning + meta: + opt-in: false nesting: type_level: warning: 1 @@ -336,238 +572,424 @@ nesting: check_nesting_in_closures_and_statements: true always_allow_one_type_in_functions: false ignore_typealiases_and_associatedtypes: false + meta: + opt-in: false nimble_operator: severity: warning + meta: + opt-in: true no_empty_block: severity: warning disabled_block_types: [] + meta: + opt-in: true no_extension_access_modifier: severity: error + meta: + opt-in: true no_fallthrough_only: severity: warning + meta: + opt-in: false no_grouping_extension: severity: warning + meta: + opt-in: true no_magic_numbers: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true no_space_in_method_call: severity: warning + meta: + opt-in: false non_optional_string_data_conversion: severity: warning + meta: + opt-in: false non_overridable_class_declaration: severity: warning final_class_modifier: final class + meta: + opt-in: true notification_center_detachment: severity: warning + meta: + opt-in: false ns_number_init_as_function_reference: severity: warning + meta: + opt-in: false nslocalizedstring_key: severity: warning + meta: + opt-in: true nslocalizedstring_require_bundle: severity: warning + meta: + opt-in: true nsobject_prefer_isequal: severity: warning + meta: + opt-in: false number_separator: severity: warning minimum_length: 0 exclude_ranges: [] + meta: + opt-in: true object_literal: severity: warning image_literal: true color_literal: true + meta: + opt-in: true one_declaration_per_file: severity: warning + meta: + opt-in: true opening_brace: severity: warning ignore_multiline_type_headers: false ignore_multiline_statement_conditions: false ignore_multiline_function_signatures: false allow_multiline_func: false + meta: + opt-in: false operator_usage_whitespace: severity: warning lines_look_around: 2 skip_aligned_constants: true allowed_no_space_operators: ["...", "..<"] + meta: + opt-in: true operator_whitespace: severity: warning + meta: + opt-in: false optional_data_string_conversion: severity: warning + meta: + opt-in: false optional_enum_case_matching: severity: warning + meta: + opt-in: true orphaned_doc_comment: severity: warning + meta: + opt-in: false overridden_super_call: severity: warning excluded: [] included: ["*"] + meta: + opt-in: true override_in_extension: severity: warning + meta: + opt-in: true pattern_matching_keywords: severity: warning + meta: + opt-in: true period_spacing: severity: warning + meta: + opt-in: true prefer_key_path: severity: warning restrict_to_standard_functions: true + meta: + opt-in: true prefer_nimble: severity: warning + meta: + opt-in: true prefer_self_in_static_references: severity: warning + meta: + opt-in: true prefer_self_type_over_type_of_self: severity: warning + meta: + opt-in: true prefer_type_checking: severity: warning + meta: + opt-in: false prefer_zero_over_explicit_init: severity: warning + meta: + opt-in: true prefixed_toplevel_constant: severity: warning only_private: false + meta: + opt-in: true private_action: severity: warning + meta: + opt-in: true private_outlet: severity: warning allow_private_set: false + meta: + opt-in: true private_over_fileprivate: severity: warning validate_extensions: false + meta: + opt-in: false private_subject: severity: warning + meta: + opt-in: true private_swiftui_state: severity: warning + meta: + opt-in: true private_unit_test: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: false prohibited_interface_builder: severity: warning + meta: + opt-in: true prohibited_super_call: severity: warning excluded: [] included: ["*"] + meta: + opt-in: true protocol_property_accessors_order: severity: warning + meta: + opt-in: false quick_discouraged_call: severity: warning + meta: + opt-in: true quick_discouraged_focused_test: severity: warning + meta: + opt-in: true quick_discouraged_pending_test: severity: warning + meta: + opt-in: true raw_value_for_camel_cased_codable_enum: severity: warning + meta: + opt-in: true reduce_boolean: severity: warning + meta: + opt-in: false reduce_into: severity: warning + meta: + opt-in: true redundant_discardable_let: severity: warning + meta: + opt-in: false redundant_nil_coalescing: severity: warning + meta: + opt-in: true redundant_objc_attribute: severity: warning + meta: + opt-in: false redundant_optional_initialization: severity: warning + meta: + opt-in: false redundant_self_in_closure: severity: warning + meta: + opt-in: true redundant_sendable: severity: warning global_actors: [] + meta: + opt-in: false redundant_set_access_control: severity: warning + meta: + opt-in: false redundant_string_enum_value: severity: warning + meta: + opt-in: false redundant_type_annotation: severity: warning ignore_attributes: ["IBInspectable"] ignore_properties: false consider_default_literal_types_redundant: false + meta: + opt-in: true redundant_void_return: severity: warning include_closures: true + meta: + opt-in: false required_deinit: severity: warning + meta: + opt-in: true required_enum_case: {Protocol Name}: {Case Name 1}: {warning|error} {Case Name 2}: {warning|error} + meta: + opt-in: true return_arrow_whitespace: severity: warning + meta: + opt-in: false return_value_from_void_function: severity: warning + meta: + opt-in: true self_binding: severity: warning bind_identifier: "self" + meta: + opt-in: true self_in_property_initialization: severity: warning + meta: + opt-in: false shorthand_argument: severity: warning allow_until_line_after_opening_brace: 4 always_disallow_more_than_one: false always_disallow_member_access: false + meta: + opt-in: true shorthand_operator: severity: error + meta: + opt-in: false shorthand_optional_binding: severity: warning + meta: + opt-in: true single_test_class: severity: warning test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true sorted_enum_cases: severity: warning + meta: + opt-in: true sorted_first_last: severity: warning + meta: + opt-in: true sorted_imports: severity: warning grouping: names + meta: + opt-in: true statement_position: severity: warning statement_mode: default + meta: + opt-in: false static_operator: severity: warning + meta: + opt-in: true static_over_final_class: severity: warning + meta: + opt-in: false strict_fileprivate: severity: warning + meta: + opt-in: true strong_iboutlet: severity: warning + meta: + opt-in: true superfluous_disable_command: severity: warning + meta: + opt-in: false superfluous_else: severity: warning + meta: + opt-in: true switch_case_alignment: severity: warning indented_cases: false ignore_one_liners: false + meta: + opt-in: false switch_case_on_newline: severity: warning + meta: + opt-in: true syntactic_sugar: severity: warning + meta: + opt-in: false test_case_accessibility: severity: warning allowed_prefixes: [] test_parent_classes: ["QuickSpec", "XCTestCase"] + meta: + opt-in: true todo: severity: warning only: [TODO, FIXME] + meta: + opt-in: false toggle_bool: severity: warning + meta: + opt-in: true trailing_closure: severity: warning only_single_muted_parameter: false + meta: + opt-in: true trailing_comma: severity: warning mandatory_comma: false + meta: + opt-in: false trailing_newline: severity: warning + meta: + opt-in: false trailing_semicolon: severity: warning + meta: + opt-in: false trailing_whitespace: severity: warning ignores_empty_lines: false ignores_comments: true + meta: + opt-in: false type_body_length: warning: 250 error: 350 + meta: + opt-in: false type_contents_order: severity: warning order: [[case], [type_alias, associated_type], [subtype], [type_property], [instance_property], [ib_inspectable], [ib_outlet], [initializer], [type_method], [view_life_cycle_method], [ib_action], [other_method], [subscript], [deinitializer]] + meta: + opt-in: true type_name: min_length: warning: 3 @@ -580,75 +1002,139 @@ type_name: unallowed_symbols_severity: error validates_start_with_lowercase: error validate_protocols: true + meta: + opt-in: false typesafe_array_init: severity: warning + meta: + opt-in: true unavailable_condition: severity: warning + meta: + opt-in: false unavailable_function: severity: warning + meta: + opt-in: true unhandled_throwing_task: severity: error + meta: + opt-in: true unneeded_break_in_switch: severity: warning + meta: + opt-in: false unneeded_override: severity: warning affect_initializers: false + meta: + opt-in: false unneeded_parentheses_in_closure_argument: severity: warning + meta: + opt-in: true unneeded_synthesized_initializer: severity: warning + meta: + opt-in: false unowned_variable_capture: severity: warning + meta: + opt-in: true untyped_error_in_catch: severity: warning + meta: + opt-in: true unused_closure_parameter: severity: warning + meta: + opt-in: false unused_control_flow_label: severity: warning + meta: + opt-in: false unused_declaration: severity: error include_public_and_open: false related_usrs_to_skip: ["s:7SwiftUI15PreviewProviderP"] + meta: + opt-in: true unused_enumerated: severity: warning + meta: + opt-in: false unused_import: severity: warning require_explicit_imports: false allowed_transitive_imports: [] always_keep_imports: [] + meta: + opt-in: true unused_optional_binding: severity: warning ignore_optional_try: false + meta: + opt-in: false unused_parameter: severity: warning + meta: + opt-in: true unused_setter_value: severity: warning + meta: + opt-in: false valid_ibinspectable: severity: warning + meta: + opt-in: false vertical_parameter_alignment: severity: warning + meta: + opt-in: false vertical_parameter_alignment_on_call: severity: warning + meta: + opt-in: true vertical_whitespace: severity: warning max_empty_lines: 1 + meta: + opt-in: false vertical_whitespace_between_cases: severity: warning + meta: + opt-in: true vertical_whitespace_closing_braces: severity: warning only_enforce_before_trivial_lines: false + meta: + opt-in: true vertical_whitespace_opening_braces: severity: warning + meta: + opt-in: true void_function_in_ternary: severity: warning + meta: + opt-in: false void_return: severity: warning + meta: + opt-in: false weak_delegate: severity: warning + meta: + opt-in: true xct_specific_matcher: severity: warning matchers: [one-argument-asserts, two-argument-asserts] + meta: + opt-in: true xctfail_message: severity: warning + meta: + opt-in: false yoda_condition: severity: warning + meta: + opt-in: true From 9f38075eb88116fb98f137015a2a36f68fbf2b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 25 Dec 2024 23:47:15 +0100 Subject: [PATCH 132/260] Evaluate arguments to boolean values --- .../SwiftLintCoreMacros/SwiftSyntaxRule.swift | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift b/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift index 454ff194a2..eeae6367e0 100644 --- a/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift +++ b/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift @@ -19,58 +19,48 @@ enum SwiftSyntaxRule: ExtensionMacro { } """ ), - try makeExtension(dependingOn: node.foldArgument, in: context, with: """ - extension \(type) { - func preprocess(file: SwiftLintFile) -> SourceFileSyntax? { - file.foldedSyntaxTree + try node.foldArgument(context).ifTrue( + try ExtensionDeclSyntax(""" + extension \(type) { + func preprocess(file: SwiftLintFile) -> SourceFileSyntax? { + file.foldedSyntaxTree + } } - } - """ + """ + ) ), - try makeExtension(dependingOn: node.explicitRewriterArgument, in: context, with: """ - extension \(type): SwiftSyntaxCorrectableRule { - func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? { - Rewriter(configuration: configuration, file: file) + try node.explicitRewriterArgument(context).ifTrue( + try ExtensionDeclSyntax(""" + extension \(type): SwiftSyntaxCorrectableRule { + func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? { + Rewriter(configuration: configuration, file: file) + } } - } - """ + """ + ) ), ].compactMap { $0 } } - - private static func makeExtension( - dependingOn argument: ExprSyntax?, - in context: some MacroExpansionContext, - with content: SyntaxNodeString - ) throws -> ExtensionDeclSyntax? { - if let argument { - if argument.isBooleanLiteral { - if argument.isTrueLiteral { - return try ExtensionDeclSyntax(content) - } - } else { - context.diagnose(SwiftLintCoreMacroError.noBooleanLiteral.diagnose(at: argument)) - } - } - return nil - } } private extension AttributeSyntax { - var foldArgument: ExprSyntax? { - findArgument(withName: "foldExpressions") + func foldArgument(_ context: some MacroExpansionContext) -> Bool { + findArgument(withName: "foldExpressions", in: context) } - var explicitRewriterArgument: ExprSyntax? { - findArgument(withName: "explicitRewriter") + func explicitRewriterArgument(_ context: some MacroExpansionContext) -> Bool { + findArgument(withName: "explicitRewriter", in: context) } - private func findArgument(withName name: String) -> ExprSyntax? { + private func findArgument(withName name: String, in context: some MacroExpansionContext) -> Bool { if case let .argumentList(args) = arguments, let first = args.first(where: { $0.label?.text == name }) { - first.expression - } else { - nil + let expr = first.expression + if expr.isBooleanLiteral { + return expr.isTrueLiteral + } + context.diagnose(SwiftLintCoreMacroError.noBooleanLiteral.diagnose(at: expr)) } + return false } } @@ -83,3 +73,9 @@ private extension ExprSyntax { `as`(BooleanLiteralExprSyntax.self)?.literal.text == "true" } } + +private extension Bool { + func ifTrue(_ result: @autoclosure () throws -> P) rethrows -> P? { + self ? try result() : nil + } +} From ff21ff796cefd0a5fb4a9411056b4a3328617413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 26 Dec 2024 00:48:16 +0100 Subject: [PATCH 133/260] Specify rule properties in attribute --- ...nymousArgumentInMultilineClosureRule.swift | 4 +-- .../Rules/Idiomatic/ConvenienceTypeRule.swift | 4 +-- .../Idiomatic/DiscouragedAssertRule.swift | 4 +-- .../Idiomatic/DiscouragedNoneNameRule.swift | 4 +-- .../DiscouragedObjectLiteralRule.swift | 4 +-- .../DiscouragedOptionalBooleanRule.swift | 4 +-- .../DiscouragedOptionalCollectionRule.swift | 4 +-- .../Rules/Idiomatic/ExplicitACLRule.swift | 4 +-- .../Idiomatic/ExplicitEnumRawValueRule.swift | 4 +-- .../Rules/Idiomatic/ExplicitInitRule.swift | 4 +-- .../Idiomatic/ExplicitTopLevelACLRule.swift | 4 +-- .../Idiomatic/ExplicitTypeInterfaceRule.swift | 4 +-- .../ExtensionAccessModifierRule.swift | 4 +-- .../Rules/Idiomatic/FallthroughRule.swift | 4 +-- .../Idiomatic/FatalErrorMessageRule.swift | 4 +-- .../Rules/Idiomatic/ForceUnwrappingRule.swift | 4 +-- .../FunctionDefaultParameterAtEndRule.swift | 4 +-- .../ImplicitlyUnwrappedOptionalRule.swift | 4 +-- .../JoinedDefaultParameterRule.swift | 4 +-- .../Rules/Idiomatic/LegacyMultipleRule.swift | 4 +-- .../Rules/Idiomatic/LegacyObjcTypeRule.swift | 4 +-- .../Rules/Idiomatic/NimbleOperatorRule.swift | 4 +-- .../Rules/Idiomatic/NoEmptyBlockRule.swift | 4 +-- .../NoExtensionAccessModifierRule.swift | 4 +-- .../Idiomatic/NoGroupingExtensionRule.swift | 4 +-- .../Rules/Idiomatic/NoMagicNumbersRule.swift | 4 +-- .../Rules/Idiomatic/ObjectLiteralRule.swift | 4 +-- .../Idiomatic/OneDeclarationPerFileRule.swift | 4 +-- .../PatternMatchingKeywordsRule.swift | 4 +-- .../Rules/Idiomatic/PreferKeyPathRule.swift | 4 +-- .../Rules/Idiomatic/PreferNimbleRule.swift | 4 +-- .../PreferZeroOverExplicitInitRule.swift | 4 +-- .../PrivateOverFilePrivateRule.swift | 4 +-- .../RedundantNilCoalescingRule.swift | 4 +-- .../RedundantTypeAnnotationRule.swift | 4 +-- .../ReturnValueFromVoidFunctionRule.swift | 4 +-- .../ShorthandOptionalBindingRule.swift | 4 +-- .../Rules/Idiomatic/StaticOperatorRule.swift | 4 +-- .../Idiomatic/StrictFilePrivateRule.swift | 4 +-- .../Rules/Idiomatic/ToggleBoolRule.swift | 4 +-- .../Idiomatic/UnavailableFunctionRule.swift | 4 +-- .../Idiomatic/UntypedErrorInCatchRule.swift | 4 +-- .../Idiomatic/XCTSpecificMatcherRule.swift | 4 +-- .../Rules/Lint/ArrayInitRule.swift | 4 +-- .../Rules/Lint/AsyncWithoutAwaitRule.swift | 4 +-- .../Lint/BalancedXCTestLifecycleRule.swift | 4 +-- ...cardedNotificationCenterObserverRule.swift | 4 +-- .../Rules/Lint/EmptyXCTestMethodRule.swift | 4 +-- .../Lint/IBInspectableInExtensionRule.swift | 4 +-- .../Rules/Lint/IdenticalOperandsRule.swift | 4 +-- .../Rules/Lint/LowerACLThanParentRule.swift | 4 +-- .../Rules/Lint/MarkRule.swift | 2 +- .../Rules/Lint/MissingDocsRule.swift | 4 +-- .../Rules/Lint/NSLocalizedStringKeyRule.swift | 4 +-- .../NSLocalizedStringRequireBundleRule.swift | 4 +-- .../Rules/Lint/OverriddenSuperCallRule.swift | 4 +-- .../Rules/Lint/PrivateActionRule.swift | 4 +-- .../Rules/Lint/PrivateOutletRule.swift | 4 +-- .../Rules/Lint/PrivateSubjectRule.swift | 4 +-- .../PrivateSwiftUIStatePropertyRule.swift | 4 +-- .../Lint/ProhibitedInterfaceBuilderRule.swift | 4 +-- .../Rules/Lint/ProhibitedSuperRule.swift | 4 +-- .../QuickDiscouragedFocusedTestRule.swift | 4 +-- .../QuickDiscouragedPendingTestRule.swift | 4 +-- ...RawValueForCamelCasedCodableEnumRule.swift | 4 +-- .../Rules/Lint/RequiredDeinitRule.swift | 4 +-- .../Rules/Lint/RequiredEnumCaseRule.swift | 4 +-- .../Rules/Lint/StrongIBOutletRule.swift | 4 +-- .../Lint/TestCaseAccessibilityRule.swift | 4 +-- .../Lint/UnhandledThrowingTaskRule.swift | 4 +-- .../Lint/UnownedVariableCaptureRule.swift | 4 +-- .../Rules/Lint/UnusedParameterRule.swift | 4 +-- .../Rules/Lint/WeakDelegateRule.swift | 4 +-- .../Rules/Lint/YodaConditionRule.swift | 4 +-- .../EnumCaseAssociatedValuesLengthRule.swift | 4 +-- .../ContainsOverFilterCountRule.swift | 4 +-- .../ContainsOverFilterIsEmptyRule.swift | 4 +-- .../ContainsOverFirstNotNilRule.swift | 4 +-- .../ContainsOverRangeNilComparisonRule.swift | 4 +-- .../EmptyCollectionLiteralRule.swift | 4 +-- .../Rules/Performance/EmptyCountRule.swift | 4 +-- .../Rules/Performance/EmptyStringRule.swift | 4 +-- .../Rules/Performance/FinalTestCaseRule.swift | 4 +-- .../Rules/Performance/FirstWhereRule.swift | 4 +-- .../FlatMapOverMapReduceRule.swift | 4 +-- .../Rules/Performance/LastWhereRule.swift | 4 +-- .../Rules/Performance/ReduceIntoRule.swift | 4 +-- .../Performance/SortedFirstLastRule.swift | 4 +-- .../Style/AttributeNameSpacingRule.swift | 4 +-- .../Rules/Style/AttributesRule.swift | 4 +-- .../Rules/Style/ClosureSpacingRule.swift | 4 +-- .../Rules/Style/CollectionAlignmentRule.swift | 4 +-- .../ConditionalReturnsOnNewlineRule.swift | 4 +-- .../Style/ContrastedOpeningBraceRule.swift | 4 +-- .../Rules/Style/DirectReturnRule.swift | 4 +-- .../Rules/Style/ImplicitReturnRule.swift | 4 +-- .../Rules/Style/LetVarWhitespaceRule.swift | 4 +-- .../MultilineArgumentsBracketsRule.swift | 4 +-- .../Rules/Style/MultilineArgumentsRule.swift | 4 +-- .../Style/MultilineLiteralBracketsRule.swift | 4 +-- .../Rules/Style/MultilineParametersRule.swift | 4 +-- .../NonOverridableClassDeclarationRule.swift | 4 +-- .../Rules/Style/NumberSeparatorRule.swift | 4 +-- .../Rules/Style/OpeningBraceRule.swift | 4 +-- .../Style/OptionalEnumCaseMatchingRule.swift | 4 +-- .../PreferSelfInStaticReferencesRule.swift | 4 +-- .../PreferSelfTypeOverTypeOfSelfRule.swift | 4 +-- .../Style/PrefixedTopLevelConstantRule.swift | 4 +-- .../Style/RedundantSelfInClosureRule.swift | 4 +-- .../Style/ReturnArrowWhitespaceRule.swift | 4 +-- .../Rules/Style/SelfBindingRule.swift | 4 +-- .../Rules/Style/ShorthandArgumentRule.swift | 4 +-- .../Rules/Style/SortedEnumCasesRule.swift | 4 +-- .../Rules/Style/SuperfluousElseRule.swift | 4 +-- .../Rules/Style/SwitchCaseOnNewlineRule.swift | 4 +-- .../Rules/Style/TrailingClosureRule.swift | 4 +-- ...ededParenthesesInClosureArgumentRule.swift | 4 +-- ...VerticalParameterAlignmentOnCallRule.swift | 4 +-- Source/SwiftLintCore/Helpers/Macros.swift | 10 ++++-- .../SwiftLintCoreMacros/SwiftSyntaxRule.swift | 20 +++++++++++ Tests/MacroTests/SwiftSyntaxRuleTests.swift | 35 ++++++++++++++++--- 121 files changed, 294 insertions(+), 241 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/AnonymousArgumentInMultilineClosureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/AnonymousArgumentInMultilineClosureRule.swift index 332668063b..f418e5e8c4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/AnonymousArgumentInMultilineClosureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/AnonymousArgumentInMultilineClosureRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct AnonymousArgumentInMultilineClosureRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct AnonymousArgumentInMultilineClosureRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ConvenienceTypeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ConvenienceTypeRule.swift index 50c4f7002a..edf9e13d7a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ConvenienceTypeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ConvenienceTypeRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ConvenienceTypeRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ConvenienceTypeRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedAssertRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedAssertRule.swift index 266cbc3269..7ce82d7c51 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedAssertRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedAssertRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscouragedAssertRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscouragedAssertRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedNoneNameRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedNoneNameRule.swift index a496e6e87f..8983423745 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedNoneNameRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedNoneNameRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscouragedNoneNameRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscouragedNoneNameRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedObjectLiteralRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedObjectLiteralRule.swift index f818d0f2dc..40bd172131 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedObjectLiteralRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedObjectLiteralRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscouragedObjectLiteralRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscouragedObjectLiteralRule: Rule { var configuration = DiscouragedObjectLiteralConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalBooleanRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalBooleanRule.swift index 854636e5c6..6a5e951ccd 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalBooleanRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalBooleanRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscouragedOptionalBooleanRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscouragedOptionalBooleanRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift index fd998b1926..644d029802 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscouragedOptionalCollectionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscouragedOptionalCollectionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitACLRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitACLRule.swift index 0cf148a8fc..102fcb3bfe 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitACLRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitACLRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct ExplicitACLRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ExplicitACLRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitEnumRawValueRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitEnumRawValueRule.swift index 7191dbd589..c5d033db5d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitEnumRawValueRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitEnumRawValueRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ExplicitEnumRawValueRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ExplicitEnumRawValueRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift index 3a0add4f3a..d8b1acaa70 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift @@ -1,8 +1,8 @@ import SwiftSyntax import SwiftSyntaxBuilder -@SwiftSyntaxRule(explicitRewriter: true) -struct ExplicitInitRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct ExplicitInitRule: Rule { var configuration = ExplicitInitConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTopLevelACLRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTopLevelACLRule.swift index d85c33c66a..faa8698103 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTopLevelACLRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTopLevelACLRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ExplicitTopLevelACLRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ExplicitTopLevelACLRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift index ec4fc88fe6..4b96ddfb54 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ExplicitTypeInterfaceRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ExplicitTypeInterfaceRule: Rule { var configuration = ExplicitTypeInterfaceConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExtensionAccessModifierRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExtensionAccessModifierRule.swift index 3fdee1d48e..4ccb056a31 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExtensionAccessModifierRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExtensionAccessModifierRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct ExtensionAccessModifierRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ExtensionAccessModifierRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FallthroughRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FallthroughRule.swift index 6b07fe9310..99391ae5c8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FallthroughRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FallthroughRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct FallthroughRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct FallthroughRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FatalErrorMessageRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FatalErrorMessageRule.swift index 4d7af1dc8b..9099a2f11d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FatalErrorMessageRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FatalErrorMessageRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct FatalErrorMessageRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct FatalErrorMessageRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceUnwrappingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceUnwrappingRule.swift index 261f36df97..a127288d24 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceUnwrappingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceUnwrappingRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ForceUnwrappingRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ForceUnwrappingRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift index a949725fbf..80a1891995 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct FunctionDefaultParameterAtEndRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct FunctionDefaultParameterAtEndRule: Rule { var configuration = FunctionDefaultParameterAtEndConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ImplicitlyUnwrappedOptionalRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ImplicitlyUnwrappedOptionalRule.swift index 6b64ce7901..feb337f970 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ImplicitlyUnwrappedOptionalRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ImplicitlyUnwrappedOptionalRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ImplicitlyUnwrappedOptionalRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ImplicitlyUnwrappedOptionalRule: Rule { var configuration = ImplicitlyUnwrappedOptionalConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift index d5e0a435c9..c4a7110dcc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct JoinedDefaultParameterRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct JoinedDefaultParameterRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyMultipleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyMultipleRule.swift index 971647780e..e450ea58c5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyMultipleRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyMultipleRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true) -struct LegacyMultipleRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, optIn: true) +struct LegacyMultipleRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyObjcTypeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyObjcTypeRule.swift index 82da70f894..35748478e7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyObjcTypeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyObjcTypeRule.swift @@ -28,8 +28,8 @@ private let legacyObjcTypes = [ "NSUUID", ] -@SwiftSyntaxRule -struct LegacyObjcTypeRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct LegacyObjcTypeRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift index 093c569751..9538ee445b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct NimbleOperatorRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct NimbleOperatorRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift index 3691a708b6..e31b097c96 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoEmptyBlockRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct NoEmptyBlockRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct NoEmptyBlockRule: Rule { var configuration = NoEmptyBlockConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoExtensionAccessModifierRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoExtensionAccessModifierRule.swift index e6a8ae4680..d9b87f76e1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoExtensionAccessModifierRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoExtensionAccessModifierRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct NoExtensionAccessModifierRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct NoExtensionAccessModifierRule: Rule { var configuration = SeverityConfiguration(.error) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoGroupingExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoGroupingExtensionRule.swift index 60c5ebcdcb..842611fae3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoGroupingExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoGroupingExtensionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct NoGroupingExtensionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct NoGroupingExtensionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift index 85cad443b6..7cf3157800 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true) -struct NoMagicNumbersRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, optIn: true) +struct NoMagicNumbersRule: Rule { var configuration = NoMagicNumbersConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ObjectLiteralRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ObjectLiteralRule.swift index 4cb5a9eaa2..b68a8914fc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ObjectLiteralRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ObjectLiteralRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ObjectLiteralRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ObjectLiteralRule: Rule { var configuration = ObjectLiteralConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/OneDeclarationPerFileRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/OneDeclarationPerFileRule.swift index 7094c9b82f..784a4e3456 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/OneDeclarationPerFileRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/OneDeclarationPerFileRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct OneDeclarationPerFileRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct OneDeclarationPerFileRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PatternMatchingKeywordsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PatternMatchingKeywordsRule.swift index 3bf67d9400..0e1510b976 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PatternMatchingKeywordsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PatternMatchingKeywordsRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PatternMatchingKeywordsRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PatternMatchingKeywordsRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift index 6f3c2819f7..18f7e1e092 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct PreferKeyPathRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct PreferKeyPathRule: Rule { var configuration = PreferKeyPathConfiguration() private static let extendedMode = ["restrict_to_standard_functions": false] diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferNimbleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferNimbleRule.swift index 18d9d28952..25c09d9cb1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferNimbleRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferNimbleRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PreferNimbleRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PreferNimbleRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift index c9f0bd9064..96e31c2694 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct PreferZeroOverExplicitInitRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct PreferZeroOverExplicitInitRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PrivateOverFilePrivateRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PrivateOverFilePrivateRule.swift index f0ffcc60b7..28cb4e9ec8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PrivateOverFilePrivateRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PrivateOverFilePrivateRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PrivateOverFilePrivateRule: SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true) +struct PrivateOverFilePrivateRule: Rule { var configuration = PrivateOverFilePrivateConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift index 093556e41d..22cc886d96 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct RedundantNilCoalescingRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct RedundantNilCoalescingRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift index ac7b3047bc..be5c19c0e7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantTypeAnnotationRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct RedundantTypeAnnotationRule: Rule { var configuration = RedundantTypeAnnotationConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift index 7b033ecb4d..72148bb912 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct ReturnValueFromVoidFunctionRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct ReturnValueFromVoidFunctionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift index 98a1187298..f29726d7b6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct ShorthandOptionalBindingRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct ShorthandOptionalBindingRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StaticOperatorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StaticOperatorRule.swift index 419085b2b8..54da156dcd 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StaticOperatorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StaticOperatorRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct StaticOperatorRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct StaticOperatorRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StrictFilePrivateRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StrictFilePrivateRule.swift index 918999604b..4a7e12bfdf 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StrictFilePrivateRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/StrictFilePrivateRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct StrictFilePrivateRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct StrictFilePrivateRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift index cc97758ccf..968c799932 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift @@ -1,8 +1,8 @@ import SwiftSyntax import SwiftSyntaxBuilder -@SwiftSyntaxRule(explicitRewriter: true) -struct ToggleBoolRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct ToggleBoolRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnavailableFunctionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnavailableFunctionRule.swift index ca5959a666..d7ab62bceb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnavailableFunctionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnavailableFunctionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct UnavailableFunctionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct UnavailableFunctionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift index fb793a1406..58ec4b9e9f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct UntypedErrorInCatchRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct UntypedErrorInCatchRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/XCTSpecificMatcherRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/XCTSpecificMatcherRule.swift index 2a762a01b9..ac41e13867 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/XCTSpecificMatcherRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/XCTSpecificMatcherRule.swift @@ -1,8 +1,8 @@ import SwiftOperators import SwiftSyntax -@SwiftSyntaxRule -struct XCTSpecificMatcherRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct XCTSpecificMatcherRule: Rule { var configuration = XCTSpecificMatcherConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift index 9d36b2c15d..d359c6340a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/ArrayInitRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ArrayInitRule: OptInRule, @unchecked Sendable { +@SwiftSyntaxRule(optIn: true) +struct ArrayInitRule: Rule, @unchecked Sendable { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift index 0604a9fc60..0268d68813 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct AsyncWithoutAwaitRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct AsyncWithoutAwaitRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/BalancedXCTestLifecycleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/BalancedXCTestLifecycleRule.swift index 9c29b21dd0..0b4401664b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/BalancedXCTestLifecycleRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/BalancedXCTestLifecycleRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct BalancedXCTestLifecycleRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct BalancedXCTestLifecycleRule: Rule { var configuration = BalancedXCTestLifecycleConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/DiscardedNotificationCenterObserverRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/DiscardedNotificationCenterObserverRule.swift index 5b42b2a113..d6a8db978b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/DiscardedNotificationCenterObserverRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/DiscardedNotificationCenterObserverRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct DiscardedNotificationCenterObserverRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct DiscardedNotificationCenterObserverRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/EmptyXCTestMethodRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/EmptyXCTestMethodRule.swift index aa7f085211..60883d23ad 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/EmptyXCTestMethodRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/EmptyXCTestMethodRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct EmptyXCTestMethodRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct EmptyXCTestMethodRule: Rule { var configuration = EmptyXCTestMethodConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/IBInspectableInExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/IBInspectableInExtensionRule.swift index 686edfdf07..759e254175 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/IBInspectableInExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/IBInspectableInExtensionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct IBInspectableInExtensionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct IBInspectableInExtensionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/IdenticalOperandsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/IdenticalOperandsRule.swift index 4849b0570c..b3497155e3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/IdenticalOperandsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/IdenticalOperandsRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true) -struct IdenticalOperandsRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, optIn: true) +struct IdenticalOperandsRule: Rule { var configuration = SeverityConfiguration(.warning) private static let operators = ["==", "!=", "===", "!==", ">", ">=", "<", "<="] diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift index 624ebf0369..c6745cade3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct LowerACLThanParentRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct LowerACLThanParentRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift index e7b92a8d17..745ccfcfeb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift @@ -2,7 +2,7 @@ import Foundation import SwiftSyntax @SwiftSyntaxRule(explicitRewriter: true) -struct MarkRule: CorrectableRule { +struct MarkRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/MissingDocsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/MissingDocsRule.swift index ed0cd61822..2a8c744f1e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/MissingDocsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/MissingDocsRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct MissingDocsRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct MissingDocsRule: Rule { var configuration = MissingDocsConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringKeyRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringKeyRule.swift index 77fb9b7ed1..2ddba2684d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringKeyRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringKeyRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct NSLocalizedStringKeyRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct NSLocalizedStringKeyRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringRequireBundleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringRequireBundleRule.swift index 22a515971a..21ab2bb3bb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringRequireBundleRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/NSLocalizedStringRequireBundleRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct NSLocalizedStringRequireBundleRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct NSLocalizedStringRequireBundleRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/OverriddenSuperCallRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/OverriddenSuperCallRule.swift index 84f8c0d3a2..9844311b37 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/OverriddenSuperCallRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/OverriddenSuperCallRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct OverriddenSuperCallRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct OverriddenSuperCallRule: Rule { var configuration = OverriddenSuperCallConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateActionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateActionRule.swift index 6e2a40a1f1..5c132e349e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateActionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateActionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PrivateActionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PrivateActionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateOutletRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateOutletRule.swift index cadc2474d2..614785a8f1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateOutletRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateOutletRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PrivateOutletRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PrivateOutletRule: Rule { var configuration = PrivateOutletConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSubjectRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSubjectRule.swift index 88aeb5a1c4..d91c7ba410 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSubjectRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSubjectRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PrivateSubjectRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PrivateSubjectRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift index a9cc469784..e3d80657cb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift @@ -10,8 +10,8 @@ import SwiftSyntax /// /// Declare state and state objects as private to prevent setting them from a memberwise initializer, /// which can conflict with the storage management that SwiftUI provides: -@SwiftSyntaxRule(explicitRewriter: true) -struct PrivateSwiftUIStatePropertyRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct PrivateSwiftUIStatePropertyRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedInterfaceBuilderRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedInterfaceBuilderRule.swift index 662f12c961..ebe95096a3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedInterfaceBuilderRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedInterfaceBuilderRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ProhibitedInterfaceBuilderRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ProhibitedInterfaceBuilderRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedSuperRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedSuperRule.swift index f19d2ba4fe..34bff27379 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedSuperRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/ProhibitedSuperRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ProhibitedSuperRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ProhibitedSuperRule: Rule { var configuration = ProhibitedSuperConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedFocusedTestRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedFocusedTestRule.swift index e04c831aba..5ac5b32996 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedFocusedTestRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedFocusedTestRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct QuickDiscouragedFocusedTestRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct QuickDiscouragedFocusedTestRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedPendingTestRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedPendingTestRule.swift index 14c4bb8acc..97b4c1c26b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedPendingTestRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/QuickDiscouragedPendingTestRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct QuickDiscouragedPendingTestRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct QuickDiscouragedPendingTestRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift index 7144b5a374..c4044d1783 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct RawValueForCamelCasedCodableEnumRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct RawValueForCamelCasedCodableEnumRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredDeinitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredDeinitRule.swift index 5308a04d39..972c325fa2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredDeinitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredDeinitRule.swift @@ -6,8 +6,8 @@ import SwiftSyntax /// of objects and the deinit should print a message or remove its instance from a /// list of allocations. Even having an empty deinit method is useful to provide /// a place to put a breakpoint when chasing down leaks. -@SwiftSyntaxRule -struct RequiredDeinitRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct RequiredDeinitRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredEnumCaseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredEnumCaseRule.swift index e702deee4a..c39b56b2df 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredEnumCaseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RequiredEnumCaseRule.swift @@ -67,8 +67,8 @@ import SwiftSyntax /// case accountCreated /// } /// ```` -@SwiftSyntaxRule -struct RequiredEnumCaseRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct RequiredEnumCaseRule: Rule { var configuration = RequiredEnumCaseConfiguration() private static let exampleConfiguration = [ diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift index 8a88d4f866..790f37329f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct StrongIBOutletRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct StrongIBOutletRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift index f68829834d..e1625b1239 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift @@ -1,8 +1,8 @@ import Foundation import SwiftSyntax -@SwiftSyntaxRule -struct TestCaseAccessibilityRule: OptInRule, SubstitutionCorrectableRule { +@SwiftSyntaxRule(optIn: true) +struct TestCaseAccessibilityRule: Rule, SubstitutionCorrectableRule { var configuration = TestCaseAccessibilityConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnhandledThrowingTaskRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnhandledThrowingTaskRule.swift index a0cf64a5b0..ed63c8653b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnhandledThrowingTaskRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnhandledThrowingTaskRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct UnhandledThrowingTaskRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct UnhandledThrowingTaskRule: Rule { var configuration = SeverityConfiguration(.error) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift index a5af1c0b5c..f66ce298cc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct UnownedVariableCaptureRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct UnownedVariableCaptureRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedParameterRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedParameterRule.swift index 70cfe125b3..b7e1329fef 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedParameterRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedParameterRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct UnusedParameterRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct UnusedParameterRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/WeakDelegateRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/WeakDelegateRule.swift index 42bf2f3540..709ec62e7e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/WeakDelegateRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/WeakDelegateRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct WeakDelegateRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct WeakDelegateRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/YodaConditionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/YodaConditionRule.swift index afff97830c..8289db7f38 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/YodaConditionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/YodaConditionRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct YodaConditionRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct YodaConditionRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Metrics/EnumCaseAssociatedValuesLengthRule.swift b/Source/SwiftLintBuiltInRules/Rules/Metrics/EnumCaseAssociatedValuesLengthRule.swift index 5804d2a377..db3c42d4b7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Metrics/EnumCaseAssociatedValuesLengthRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Metrics/EnumCaseAssociatedValuesLengthRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct EnumCaseAssociatedValuesLengthRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct EnumCaseAssociatedValuesLengthRule: Rule { var configuration = SeverityLevelsConfiguration(warning: 5, error: 6) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterCountRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterCountRule.swift index ba5eae2559..e6970385c4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterCountRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterCountRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ContainsOverFilterCountRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ContainsOverFilterCountRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterIsEmptyRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterIsEmptyRule.swift index d2546c70cf..730196d42c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterIsEmptyRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFilterIsEmptyRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ContainsOverFilterIsEmptyRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ContainsOverFilterIsEmptyRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFirstNotNilRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFirstNotNilRule.swift index fbe945218e..22d08b3c35 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFirstNotNilRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverFirstNotNilRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true) -struct ContainsOverFirstNotNilRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, optIn: true) +struct ContainsOverFirstNotNilRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverRangeNilComparisonRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverRangeNilComparisonRule.swift index a0f5857480..44ffc9db21 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverRangeNilComparisonRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/ContainsOverRangeNilComparisonRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true) -struct ContainsOverRangeNilComparisonRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, optIn: true) +struct ContainsOverRangeNilComparisonRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCollectionLiteralRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCollectionLiteralRule.swift index 2d74682cd3..a45100697b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCollectionLiteralRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCollectionLiteralRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct EmptyCollectionLiteralRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct EmptyCollectionLiteralRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift index c1ffce14f7..3901237f2a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(foldExpressions: true, explicitRewriter: true) -struct EmptyCountRule: OptInRule { +@SwiftSyntaxRule(foldExpressions: true, explicitRewriter: true, optIn: true) +struct EmptyCountRule: Rule { var configuration = EmptyCountConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyStringRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyStringRule.swift index ad199a36a4..0f6576cbc9 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyStringRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyStringRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct EmptyStringRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct EmptyStringRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift index 5afde4bc10..b18c5ab579 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct FinalTestCaseRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct FinalTestCaseRule: Rule { var configuration = FinalTestCaseConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/FirstWhereRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/FirstWhereRule.swift index 4576af9f98..e3ae47c1ca 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/FirstWhereRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/FirstWhereRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct FirstWhereRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct FirstWhereRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/FlatMapOverMapReduceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/FlatMapOverMapReduceRule.swift index 650990e7cb..b45562bcbb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/FlatMapOverMapReduceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/FlatMapOverMapReduceRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct FlatMapOverMapReduceRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct FlatMapOverMapReduceRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/LastWhereRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/LastWhereRule.swift index 577bc5d34e..f053764d5e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/LastWhereRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/LastWhereRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct LastWhereRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct LastWhereRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/ReduceIntoRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/ReduceIntoRule.swift index d683a2d29e..a179edf276 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/ReduceIntoRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/ReduceIntoRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ReduceIntoRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ReduceIntoRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/SortedFirstLastRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/SortedFirstLastRule.swift index ebe76cb73a..df1b44f3b1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/SortedFirstLastRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/SortedFirstLastRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct SortedFirstLastRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct SortedFirstLastRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift index 50ca9d4b92..8655186447 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/AttributeNameSpacingRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct AttributeNameSpacingRule: SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true) +struct AttributeNameSpacingRule: Rule { var configuration = SeverityConfiguration(.error) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/AttributesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/AttributesRule.swift index ed24df070e..95fce3a98a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/AttributesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/AttributesRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct AttributesRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct AttributesRule: Rule { var configuration = AttributesConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift index 55da2a8d0f..e3cc4e0243 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct ClosureSpacingRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct ClosureSpacingRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/CollectionAlignmentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/CollectionAlignmentRule.swift index 916f9bd2c7..893e912a3c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/CollectionAlignmentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/CollectionAlignmentRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct CollectionAlignmentRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct CollectionAlignmentRule: Rule { var configuration = CollectionAlignmentConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ConditionalReturnsOnNewlineRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ConditionalReturnsOnNewlineRule.swift index 7c95293376..92d28780d2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ConditionalReturnsOnNewlineRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ConditionalReturnsOnNewlineRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ConditionalReturnsOnNewlineRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ConditionalReturnsOnNewlineRule: Rule { var configuration = ConditionalReturnsOnNewlineConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift index ba4ceb083b..10dcabc645 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ContrastedOpeningBraceRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct ContrastedOpeningBraceRule: OptInRule, SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct ContrastedOpeningBraceRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift index 591b7e1d6a..95852f332b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct DirectReturnRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct DirectReturnRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ImplicitReturnRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ImplicitReturnRule.swift index f58d9db366..92aacced6d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ImplicitReturnRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ImplicitReturnRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ImplicitReturnRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct ImplicitReturnRule: Rule { var configuration = ImplicitReturnConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/LetVarWhitespaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/LetVarWhitespaceRule.swift index 018ad886e1..fbe5955695 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/LetVarWhitespaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/LetVarWhitespaceRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct LetVarWhitespaceRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct LetVarWhitespaceRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsBracketsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsBracketsRule.swift index 35cd7feee8..46bdd484ef 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsBracketsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsBracketsRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct MultilineArgumentsBracketsRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct MultilineArgumentsBracketsRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsRule.swift index a5e50b31a7..c9d91c2ea5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineArgumentsRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct MultilineArgumentsRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct MultilineArgumentsRule: Rule { var configuration = MultilineArgumentsConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineLiteralBracketsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineLiteralBracketsRule.swift index 38264908d5..51c134d8db 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineLiteralBracketsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineLiteralBracketsRule.swift @@ -1,8 +1,8 @@ import Foundation import SwiftSyntax -@SwiftSyntaxRule -struct MultilineLiteralBracketsRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct MultilineLiteralBracketsRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift index 72f25152c0..7d715364a8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/MultilineParametersRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct MultilineParametersRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct MultilineParametersRule: Rule { var configuration = MultilineParametersConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/NonOverridableClassDeclarationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/NonOverridableClassDeclarationRule.swift index a4ede58399..ec691bf824 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/NonOverridableClassDeclarationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/NonOverridableClassDeclarationRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct NonOverridableClassDeclarationRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct NonOverridableClassDeclarationRule: Rule { var configuration = NonOverridableClassDeclarationConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift index a35b0aae41..3158e79a2f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift @@ -1,8 +1,8 @@ import Foundation import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct NumberSeparatorRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct NumberSeparatorRule: Rule { var configuration = NumberSeparatorConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift index fe5b55f517..7205157801 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OpeningBraceRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct OpeningBraceRule: SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true) +struct OpeningBraceRule: Rule { var configuration = OpeningBraceConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift index 5d475ef56f..6a812aa5b2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct OptionalEnumCaseMatchingRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct OptionalEnumCaseMatchingRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift index c9b9e62b65..fd65e1e32b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PreferSelfInStaticReferencesRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct PreferSelfInStaticReferencesRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift index 6266949b9d..3ecd15a5da 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct PreferSelfTypeOverTypeOfSelfRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct PreferSelfTypeOverTypeOfSelfRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PrefixedTopLevelConstantRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PrefixedTopLevelConstantRule.swift index 74e10f47f1..79de73d867 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PrefixedTopLevelConstantRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PrefixedTopLevelConstantRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct PrefixedTopLevelConstantRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct PrefixedTopLevelConstantRule: Rule { var configuration = PrefixedTopLevelConstantConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/RedundantSelfInClosureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/RedundantSelfInClosureRule.swift index 17dde72a66..49b1f55106 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/RedundantSelfInClosureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/RedundantSelfInClosureRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct RedundantSelfInClosureRule: SwiftSyntaxCorrectableRule, OptInRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct RedundantSelfInClosureRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ReturnArrowWhitespaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ReturnArrowWhitespaceRule.swift index 7248f2ff24..971b03f014 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ReturnArrowWhitespaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ReturnArrowWhitespaceRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct ReturnArrowWhitespaceRule: SwiftSyntaxCorrectableRule { +@SwiftSyntaxRule(correctable: true) +struct ReturnArrowWhitespaceRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift index 25e2672917..2d9716754a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift @@ -2,8 +2,8 @@ import SwiftSyntax // MARK: - SelfBindingRule -@SwiftSyntaxRule(explicitRewriter: true) -struct SelfBindingRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct SelfBindingRule: Rule { var configuration = SelfBindingConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ShorthandArgumentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ShorthandArgumentRule.swift index f8068dafd2..f7eeb9c310 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ShorthandArgumentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ShorthandArgumentRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule -struct ShorthandArgumentRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct ShorthandArgumentRule: Rule { var configuration = ShorthandArgumentConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedEnumCasesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedEnumCasesRule.swift index 31c8b3b087..f35edea7f5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedEnumCasesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedEnumCasesRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct SortedEnumCasesRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct SortedEnumCasesRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift index d15ddd7322..74b02f875a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct SuperfluousElseRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct SuperfluousElseRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SwitchCaseOnNewlineRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SwitchCaseOnNewlineRule.swift index 71159534e5..8e4f03b265 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SwitchCaseOnNewlineRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SwitchCaseOnNewlineRule.swift @@ -8,8 +8,8 @@ private func wrapInSwitch(_ str: String, file: StaticString = #filePath, line: U """, file: file, line: line) } -@SwiftSyntaxRule -struct SwitchCaseOnNewlineRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct SwitchCaseOnNewlineRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift index 204d602568..a2bfa1442d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift @@ -1,8 +1,8 @@ import SwiftLintCore import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct TrailingClosureRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct TrailingClosureRule: Rule { var configuration = TrailingClosureConfiguration() static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift index 91c362bee6..39e767b9ea 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) -struct UnneededParenthesesInClosureArgumentRule: OptInRule { +@SwiftSyntaxRule(explicitRewriter: true, optIn: true) +struct UnneededParenthesesInClosureArgumentRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalParameterAlignmentOnCallRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalParameterAlignmentOnCallRule.swift index a7b9bedc38..b3651e765c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalParameterAlignmentOnCallRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalParameterAlignmentOnCallRule.swift @@ -1,7 +1,7 @@ import SwiftSyntax -@SwiftSyntaxRule -struct VerticalParameterAlignmentOnCallRule: OptInRule { +@SwiftSyntaxRule(optIn: true) +struct VerticalParameterAlignmentOnCallRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( diff --git a/Source/SwiftLintCore/Helpers/Macros.swift b/Source/SwiftLintCore/Helpers/Macros.swift index 7d6270d2ac..a6e099764c 100644 --- a/Source/SwiftLintCore/Helpers/Macros.swift +++ b/Source/SwiftLintCore/Helpers/Macros.swift @@ -52,12 +52,18 @@ public macro MakeAcceptableByConfigurationElement() = #externalMacro( /// - explicitRewriter: Set it to `true` to add a `makeRewriter(file:)` implementation which creates a rewriter /// defined in the rule struct. In this case, the rule automatically conforms to /// ``SwiftSyntaxCorrectableRule``. +/// - correctable: Set it to `true` to make the rule conform to ``SwiftSyntaxCorrectableRule`` without an explicit +/// rewriter. +/// - optIn: Set it to `true` to make the rule conform to ``OptInRule``. @attached( extension, - conformances: SwiftSyntaxRule, SwiftSyntaxCorrectableRule, + conformances: SwiftSyntaxRule, SwiftSyntaxCorrectableRule, OptInRule, Rule, names: named(makeVisitor(file:)), named(preprocess(file:)), named(makeRewriter(file:)) ) -public macro SwiftSyntaxRule(foldExpressions: Bool = false, explicitRewriter: Bool = false) = #externalMacro( +public macro SwiftSyntaxRule(foldExpressions: Bool = false, + explicitRewriter: Bool = false, + correctable: Bool = false, + optIn: Bool = false) = #externalMacro( module: "SwiftLintCoreMacros", type: "SwiftSyntaxRule" ) diff --git a/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift b/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift index eeae6367e0..0e8cb8c06a 100644 --- a/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift +++ b/Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift @@ -39,6 +39,18 @@ enum SwiftSyntaxRule: ExtensionMacro { """ ) ), + try (node.correctableArgument(context) && !node.explicitRewriterArgument(context)).ifTrue( + try ExtensionDeclSyntax(""" + extension \(type): SwiftSyntaxCorrectableRule {} + """ + ) + ), + try node.optInArgument(context).ifTrue( + try ExtensionDeclSyntax(""" + extension \(type): OptInRule {} + """ + ) + ), ].compactMap { $0 } } } @@ -52,6 +64,14 @@ private extension AttributeSyntax { findArgument(withName: "explicitRewriter", in: context) } + func correctableArgument(_ context: some MacroExpansionContext) -> Bool { + findArgument(withName: "correctable", in: context) + } + + func optInArgument(_ context: some MacroExpansionContext) -> Bool { + findArgument(withName: "optIn", in: context) + } + private func findArgument(withName name: String, in context: some MacroExpansionContext) -> Bool { if case let .argumentList(args) = arguments, let first = args.first(where: { $0.label?.text == name }) { let expr = first.expression diff --git a/Tests/MacroTests/SwiftSyntaxRuleTests.swift b/Tests/MacroTests/SwiftSyntaxRuleTests.swift index d5a7dd28d7..9461711cfa 100644 --- a/Tests/MacroTests/SwiftSyntaxRuleTests.swift +++ b/Tests/MacroTests/SwiftSyntaxRuleTests.swift @@ -29,7 +29,7 @@ final class SwiftSyntaxRuleTests: XCTestCase { func testFalseArguments() { assertMacroExpansion( """ - @SwiftSyntaxRule(foldExpressions: false, explicitRewriter: false) + @SwiftSyntaxRule(foldExpressions: false, explicitRewriter: false, correctable: false, optIn: false) struct Hello {} """, expandedSource: """ @@ -48,7 +48,7 @@ final class SwiftSyntaxRuleTests: XCTestCase { func testTrueArguments() { assertMacroExpansion( """ - @SwiftSyntaxRule(foldExpressions: true, explicitRewriter: true) + @SwiftSyntaxRule(foldExpressions: true, explicitRewriter: true, correctable: true, optIn: true) struct Hello {} """, expandedSource: """ @@ -71,6 +71,31 @@ final class SwiftSyntaxRuleTests: XCTestCase { Rewriter(configuration: configuration, file: file) } } + + extension Hello: OptInRule { + } + """, + macros: macros + ) + } + + func testCorrectableWithoutExplcitRewriter() { + assertMacroExpansion( + """ + @SwiftSyntaxRule(correctable: true) + struct Hello {} + """, + expandedSource: """ + struct Hello {} + + extension Hello: SwiftSyntaxRule { + func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor { + Visitor(configuration: configuration, file: file) + } + } + + extension Hello: SwiftSyntaxCorrectableRule { + } """, macros: macros ) @@ -80,7 +105,7 @@ final class SwiftSyntaxRuleTests: XCTestCase { // Fail with a diagnostic because the macro definition explicitly requires bool arguments. assertMacroExpansion( """ - @SwiftSyntaxRule(foldExpressions: variable, explicitRewriter: variable) + @SwiftSyntaxRule(foldExpressions: arg, explicitRewriter: arg, correctable: arg, optIn: arg) struct Hello {} """, expandedSource: """ @@ -94,7 +119,9 @@ final class SwiftSyntaxRuleTests: XCTestCase { """, diagnostics: [ DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 35), - DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 63), + DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 58), + DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 76), + DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 88), ], macros: macros ) From 1d8af83b480c7bfed48e9dd658a131a319942cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 27 Dec 2024 09:58:53 +0100 Subject: [PATCH 134/260] Collect corrections together with violations (#5916) --- .../Lint/TestCaseAccessibilityRule.swift | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift index e1625b1239..d0ca0d19a4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/TestCaseAccessibilityRule.swift @@ -1,8 +1,7 @@ -import Foundation import SwiftSyntax -@SwiftSyntaxRule(optIn: true) -struct TestCaseAccessibilityRule: Rule, SubstitutionCorrectableRule { +@SwiftSyntaxRule(correctable: true, optIn: true) +struct TestCaseAccessibilityRule: Rule { var configuration = TestCaseAccessibilityConfiguration() static let description = RuleDescription( @@ -14,18 +13,6 @@ struct TestCaseAccessibilityRule: Rule, SubstitutionCorrectableRule { triggeringExamples: TestCaseAccessibilityRuleExamples.triggeringExamples, corrections: TestCaseAccessibilityRuleExamples.corrections ) - - func violationRanges(in file: SwiftLintFile) -> [NSRange] { - makeVisitor(file: file) - .walk(tree: file.syntaxTree, handler: \.violations) - .compactMap { - file.stringView.NSRange(start: $0.position, end: $0.position) - } - } - - func substitution(for violationRange: NSRange, in _: SwiftLintFile) -> (NSRange, String)? { - (violationRange, "private ") - } } private extension TestCaseAccessibilityRule { @@ -36,10 +23,17 @@ private extension TestCaseAccessibilityRule { guard !configuration.testParentClasses.isDisjoint(with: node.inheritedTypes) else { return } - violations.append( - contentsOf: XCTestClassVisitor(configuration: configuration, file: file) - .walk(tree: node.memberBlock, handler: \.violations) - ) + XCTestClassVisitor(configuration: configuration, file: file) + .walk(tree: node.memberBlock, handler: \.violations) + .forEach { violation in + let position = violation.position + violations.append( + ReasonedRuleViolation( + position: position, + correction: .init(start: position, end: position, replacement: "private ") + ) + ) + } } } From dd157e2bdfbae0042728e5a2df5a75ec2712467a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 28 Dec 2024 10:44:07 +0100 Subject: [PATCH 135/260] Use task-local variable (#5919) --- Source/SwiftLintCore/Models/Issue.swift | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index bed6671aaa..eb902b921c 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -79,11 +79,9 @@ public enum Issue: LocalizedError, Equatable { case baselineNotReadable(path: String) /// Flag to enable warnings for deprecations being printed to the console. Printing is enabled by default. - #if compiler(>=6.0) package nonisolated(unsafe) static var printDeprecationWarnings = true - #else - package static var printDeprecationWarnings = true - #endif + + @TaskLocal private static var messageConsumer: (@Sendable (String) -> Void)? /// Hook used to capture all messages normally printed to stdout and return them back to the caller. /// @@ -97,20 +95,17 @@ public enum Issue: LocalizedError, Equatable { actor Console { static var content = "" } - messageConsumer = { - Console.content += (Console.content.isEmpty ? "" : "\n") + $0 - } defer { - messageConsumer = nil Console.content = "" } - try runner() + try $messageConsumer.withValue( + { Console.content += (Console.content.isEmpty ? "" : "\n") + $0 }, + operation: runner + ) await Task.yield() return Console.content } - @MainActor private static var messageConsumer: (@Sendable (String) -> Void)? - /// Wraps any `Error` into a `SwiftLintError.genericWarning` if it is not already a `SwiftLintError`. /// /// - parameter error: Any `Error`. @@ -142,7 +137,7 @@ public enum Issue: LocalizedError, Equatable { if case .ruleDeprecated = self, !Self.printDeprecationWarnings { return } - Task { @MainActor in + Task(priority: .high) { @MainActor in if let consumer = Self.messageConsumer { consumer(errorDescription) } else { From bef8acfb0e8a27eca83063f8d5f2b55258d4ced5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 28 Dec 2024 18:54:23 +0100 Subject: [PATCH 136/260] Avoid NSRegularExpression in configurations (#5921) --- .../FileHeaderConfiguration.swift | 26 ++++++++++++------- .../NSRegularExpression+SwiftLint.swift | 22 ++++++++++++++-- Source/SwiftLintCore/Models/Issue.swift | 5 ++++ .../RegexConfiguration.swift | 18 ++++++------- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileHeaderConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileHeaderConfiguration.swift index 2090c23ba6..e23e265ffd 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileHeaderConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/FileHeaderConfiguration.swift @@ -21,8 +21,8 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration { @ConfigurationElement(key: "forbidden_pattern") private var forbiddenPattern: String? - private var _forbiddenRegex: NSRegularExpression? - private var _requiredRegex: NSRegularExpression? + private var _forbiddenRegex: RegularExpression? + private var _requiredRegex: RegularExpression? private static let defaultRegex = regex("\\bCopyright\\b", options: [.caseInsensitive]) @@ -37,26 +37,32 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration { if let requiredString = configuration[$requiredString.key] { self.requiredString = requiredString if !requiredString.contains(Self.fileNamePlaceholder) { - _requiredRegex = try NSRegularExpression(pattern: requiredString, - options: Self.stringRegexOptions) + _requiredRegex = try .from( + pattern: requiredString, + options: Self.stringRegexOptions, + for: Parent.identifier + ) } } else if let requiredPattern = configuration[$requiredPattern.key] { self.requiredPattern = requiredPattern if !requiredPattern.contains(Self.fileNamePlaceholder) { - _requiredRegex = try .cached(pattern: requiredPattern) + _requiredRegex = try .from(pattern: requiredPattern, for: Parent.identifier) } } if let forbiddenString = configuration[$forbiddenString.key] { self.forbiddenString = forbiddenString if !forbiddenString.contains(Self.fileNamePlaceholder) { - _forbiddenRegex = try NSRegularExpression(pattern: forbiddenString, - options: Self.stringRegexOptions) + _forbiddenRegex = try .from( + pattern: forbiddenString, + options: Self.stringRegexOptions, + for: Parent.identifier + ) } } else if let forbiddenPattern = configuration[$forbiddenPattern.key] { self.forbiddenPattern = forbiddenPattern if !forbiddenPattern.contains(Self.fileNamePlaceholder) { - _forbiddenRegex = try .cached(pattern: forbiddenPattern) + _forbiddenRegex = try .from(pattern: forbiddenPattern, for: Parent.identifier) } } @@ -96,7 +102,7 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration { func forbiddenRegex(for file: SwiftLintFile) -> NSRegularExpression? { if _forbiddenRegex != nil { - return _forbiddenRegex + return _forbiddenRegex?.regex } if let regex = forbiddenString.flatMap({ regexFromString(for: file, using: $0) }) { @@ -116,7 +122,7 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration { func requiredRegex(for file: SwiftLintFile) -> NSRegularExpression? { if _requiredRegex != nil { - return _requiredRegex + return _requiredRegex?.regex } if let regex = requiredString.flatMap({ regexFromString(for: file, using: $0) }) { diff --git a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift index fc482a379d..76cc1272e9 100644 --- a/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/NSRegularExpression+SwiftLint.swift @@ -7,9 +7,10 @@ private nonisolated(unsafe) var regexCache = [RegexCacheKey: NSRegularExpression public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLiteral, Sendable { public let regex: NSRegularExpression - public init(pattern: String, options _: NSRegularExpression.Options? = nil) throws { - regex = try .cached(pattern: pattern) + public init(pattern: String, options: NSRegularExpression.Options? = nil) throws { + regex = try .cached(pattern: pattern, options: options) } + public init(stringLiteral value: String) { // swiftlint:disable:next force_try try! self.init(pattern: value) @@ -22,6 +23,23 @@ public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLitera public static func < (lhs: Self, rhs: Self) -> Bool { lhs.pattern < rhs.pattern } + + /// Creates a `RegularExpression` from a pattern and options. + /// + /// - Parameters: + /// - pattern: The pattern to compile into a regular expression. + /// - options: The options to use when compiling the regular expression. + /// - ruleID: The identifier of the rule that is using this regular expression in its configuration. + /// - Returns: A `RegularExpression` instance. + public static func from(pattern: String, + options: NSRegularExpression.Options? = nil, + for ruleID: String) throws -> Self { + do { + return try Self(pattern: pattern, options: options) + } catch { + throw Issue.invalidRegexPattern(ruleID: ruleID, pattern: pattern) + } + } } private struct RegexCacheKey: Hashable { diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index eb902b921c..9c120d1542 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -5,6 +5,9 @@ public enum Issue: LocalizedError, Equatable { /// The configuration didn't match internal expectations. case invalidConfiguration(ruleID: String, message: String? = nil) + /// Issued when a regular expression pattern is invalid. + case invalidRegexPattern(ruleID: String, pattern: String) + /// Issued when an option is deprecated. Suggests an alternative optionally. case deprecatedConfigurationOption(ruleID: String, key: String, alternative: String? = nil) @@ -151,6 +154,8 @@ public enum Issue: LocalizedError, Equatable { case let .invalidConfiguration(id, message): let message = if let message { ": \(message)" } else { "." } return "Invalid configuration for '\(id)' rule\(message) Falling back to default." + case let .invalidRegexPattern(id, pattern): + return "Invalid regular expression pattern '\(pattern)' used to configure '\(id)' rule." case let .deprecatedConfigurationOption(id, key, alternative): let baseMessage = "Configuration option '\(key)' in '\(id)' rule is deprecated." if let alternative { diff --git a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift index 09ab2d12a1..6329261b45 100644 --- a/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift +++ b/Source/SwiftLintCore/RuleConfigurations/RegexConfiguration.swift @@ -14,9 +14,9 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, @ConfigurationElement(key: "regex") package var regex: RegularExpression! // swiftlint:disable:this implicitly_unwrapped_optional /// Regular expressions to include when matching the file path. - public var included: [NSRegularExpression] = [] + public var included: [RegularExpression] = [] /// Regular expressions to exclude when matching the file path. - public var excluded: [NSRegularExpression] = [] + public var excluded: [RegularExpression] = [] /// The syntax kinds to exclude from matches. If the regex matched syntax kinds from this list, it would /// be ignored and not count as a rule violation. public var excludedMatchKinds = Set() @@ -63,21 +63,21 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, throw Issue.invalidConfiguration(ruleID: Parent.identifier) } - regex = try RegularExpression(pattern: regexString) + regex = try .from(pattern: regexString, for: Parent.identifier) if let includedString = configurationDict["included"] as? String { - included = [try .cached(pattern: includedString)] + included = [try .from(pattern: includedString, for: Parent.identifier)] } else if let includedArray = configurationDict["included"] as? [String] { included = try includedArray.map { pattern in - try .cached(pattern: pattern) + try .from(pattern: pattern, for: Parent.identifier) } } if let excludedString = configurationDict["excluded"] as? String { - excluded = [try .cached(pattern: excludedString)] + excluded = [try .from(pattern: excludedString, for: Parent.identifier)] } else if let excludedArray = configurationDict["excluded"] as? [String] { excluded = try excludedArray.map { pattern in - try .cached(pattern: pattern) + try .from(pattern: pattern, for: Parent.identifier) } } @@ -107,7 +107,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, package func shouldValidate(filePath: String) -> Bool { let pathRange = filePath.fullNSRange let isIncluded = included.isEmpty || included.contains { regex in - regex.firstMatch(in: filePath, range: pathRange) != nil + regex.regex.firstMatch(in: filePath, range: pathRange) != nil } guard isIncluded else { @@ -115,7 +115,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, } return excluded.allSatisfy { regex in - regex.firstMatch(in: filePath, range: pathRange) == nil + regex.regex.firstMatch(in: filePath, range: pathRange) == nil } } From 15b285527ab680dab9ba5907e24f4ef96ec3dac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Dec 2024 12:26:46 +0100 Subject: [PATCH 137/260] Separate built-in rule tests from framework tests (#5924) * Short names for test modules * Lint plugins and `Package.swift` in integration tests * Simplify and merge file groups in Bazel * Move common functions to `TestHelpers` --- .sourcery/GeneratedTests.stencil | 2 +- .swiftlint.yml | 3 +- BUILD | 5 +- Package.swift | 26 +++++-- Tests/BUILD | 76 ++++++++++++------- .../AttributesRuleTests.swift | 1 + .../BlanketDisableCommandRuleTests.swift | 1 + ...hildOptionSeverityConfigurationTests.swift | 1 + .../CollectionAlignmentRuleTests.swift | 1 + .../ColonRuleTests.swift | 1 + .../CompilerProtocolInitRuleTests.swift | 1 + .../ComputedAccessorsOrderRuleTests.swift | 1 + ...ConditionalReturnsOnNewlineRuleTests.swift | 1 + .../ContainsOverFirstNotNilRuleTests.swift | 3 +- ...clomaticComplexityConfigurationTests.swift | 1 + .../CyclomaticComplexityRuleTests.swift | 1 + .../DeploymentTargetConfigurationTests.swift | 1 + .../DeploymentTargetRuleTests.swift | 3 +- .../DiscouragedDirectInitRuleTests.swift | 1 + .../DiscouragedObjectLiteralRuleTests.swift | 1 + .../DuplicateImportsRuleTests.swift | 0 .../EmptyCountRuleTests.swift | 1 + .../ExpiringTodoRuleTests.swift | 3 +- .../ExplicitInitRuleTests.swift | 1 + ...licitTypeInterfaceConfigurationTests.swift | 1 + .../ExplicitTypeInterfaceRuleTests.swift | 1 + .../FileHeaderRuleTests.swift | 3 +- .../FileLengthRuleTests.swift | 1 + .../FileNameNoSpaceRuleTests.swift | 5 +- .../FileNameRuleTests.swift | 3 +- .../FileTypesOrderRuleTests.swift | 1 + .../FunctionBodyLengthRuleTests.swift | 3 +- .../FunctionParameterCountRuleTests.swift | 1 + .../GenericTypeNameRuleTests.swift | 1 + .../IdentifierNameRuleTests.swift | 1 + .../ImplicitGetterRuleTests.swift | 1 + .../ImplicitReturnConfigurationTests.swift | 1 + .../ImplicitReturnRuleTests.swift | 1 + ...yUnwrappedOptionalConfigurationTests.swift | 1 + ...ImplicitlyUnwrappedOptionalRuleTests.swift | 1 + .../InclusiveLanguageRuleTests.swift | 1 + .../IndentationWidthRuleTests.swift | 2 +- .../LineLengthConfigurationTests.swift | 1 + .../LineLengthRuleTests.swift | 1 + .../MissingDocsRuleTests.swift | 1 + .../MultilineArgumentsRuleTests.swift | 1 + ...ultilineParametersConfigurationTests.swift | 1 + .../NameConfigurationTests.swift | 1 + .../NestingRuleTests.swift | 1 + .../NoEmptyBlockConfigurationTests.swift | 1 + .../NumberSeparatorRuleTests.swift | 1 + .../ObjectLiteralRuleTests.swift | 1 + .../OpeningBraceRuleTests.swift | 1 + .../PreferKeyPathRuleTests.swift | 1 + .../PrefixedTopLevelConstantRuleTests.swift | 1 + .../PrivateOverFilePrivateRuleTests.swift | 1 + .../RequiredEnumCaseConfigurationTests.swift | 1 + .../DocumentedType.swift | 0 .../FileHeaderEmpty.swift | 0 .../FileNameCaseMismatch.swift | 0 .../FileNameMatchingComplex.swift | 0 .../FileNameMatchingSimple.swift | 0 .../FileNameMismatch.swift | 0 .../FileNameMissing.swift | 0 .../File Name.swift | 0 .../File+Extension.swift | 0 .../File+Test Extension.swift | 0 .../FileNameNoSpaceRuleFixtures/File.swift | 0 .../FileNameRuleFixtures/BoolExtension.swift | 0 .../BoolExtensionTests.swift | 0 .../FileNameRuleFixtures/BoolExtensions.swift | 0 .../ExtensionBool+SwiftLint.swift | 0 .../FileNameRuleFixtures/ExtensionBool.swift | 0 .../FileNameRuleFixtures/ExtensionsBool.swift | 0 .../FileNameRuleFixtures/LinuxMain.swift | 2 +- ...Multiple.Levels.Deeply.Nested.MyType.swift | 0 .../FileNameRuleFixtures/MyClass.swift | 0 .../FileNameRuleFixtures/MyStruct.swift | 0 .../FileNameRuleFixtures/MyStructf.swift | 0 .../FileNameRuleFixtures/MyType.swift | 0 .../NSString+Extension.swift | 0 .../FileNameRuleFixtures/Nested.MyType.swift | 0 .../Notification.Name+Extension.swift | 0 .../NotificationName+Extension.swift | 0 .../Notification__Name+Extension.swift | 0 .../SLBoolExtension.swift | 0 .../Resources/FileNameRuleFixtures/main.swift | 0 .../StatementPositionRuleTests.swift | 1 + .../SwitchCaseAlignmentRuleTests.swift | 1 + .../TodoRuleTests.swift | 3 +- .../TrailingClosureConfigurationTests.swift | 1 + .../TrailingClosureRuleTests.swift | 1 + .../TrailingCommaRuleTests.swift | 1 + .../TrailingWhitespaceRuleTests.swift | 1 + .../TypeContentsOrderRuleTests.swift | 1 + .../TypeNameRuleTests.swift | 1 + .../TypesafeArrayInitRuleTests.swift | 3 +- .../UnneededOverrideRuleTests.swift | 1 + .../UnusedDeclarationConfigurationTests.swift | 0 .../UnusedOptionalBindingRuleTests.swift | 1 + .../VerticalWhitespaceRuleTests.swift | 1 + .../XCTSpecificMatcherRuleTests.swift | 3 +- Tests/ExtraRulesTests/ExtraRulesTests.swift | 2 +- .../AccessControlLevelTests.swift | 0 .../BaselineTests.swift | 0 .../CodeIndentingRewriterTests.swift | 0 .../CollectingRuleTests.swift | 2 +- .../CommandTests.swift | 0 .../ConfigurationAliasesTests.swift | 0 .../ConfigurationTests+Mock.swift | 15 +--- .../ConfigurationTests+MultipleConfigs.swift | 0 .../ConfigurationTests.swift | 0 .../CustomRulesTests.swift | 5 +- .../DisableAllTests.swift | 0 .../ExampleTests.swift | 0 .../Exports.swift | 2 +- .../ExtendedNSStringTests.swift | 0 .../ExtendedStringTests.swift | 0 .../GlobTests.swift | 3 +- .../LineEndingTests.swift | 0 .../LintOrAnalyzeOptionsTests.swift | 0 .../LinterCacheTests.swift | 0 .../ModifierOrderTests.swift | 0 .../ParserDiagnosticsTests.swift | 0 .../RegexConfigurationTests.swift | 0 .../RegionTests.swift | 0 .../ReporterTests.swift | 5 +- .../Resources/CannedCSVReporterOutput.csv | 0 .../CannedCheckstyleReporterOutput.xml | 0 .../CannedCodeClimateReporterOutput.json | 0 .../Resources/CannedEmojiReporterOutput.txt | 0 ...nnedGitHubActionsLoggingReporterOutput.txt | 0 .../CannedGitLabJUnitReporterOutput.xml | 0 .../Resources/CannedHTMLReporterOutput.html | 0 .../Resources/CannedJSONReporterOutput.json | 0 .../Resources/CannedJunitReporterOutput.xml | 0 .../Resources/CannedMarkdownReporterOutput.md | 0 .../CannedRelativePathReporterOutput.txt | 0 .../Resources/CannedSARIFReporterOutput.json | 0 .../CannedSonarQubeReporterOutput.json | 0 ...annedSummaryReporterNoViolationsOutput.txt | 0 .../Resources/CannedSummaryReporterOutput.txt | 0 .../Resources/CannedXcodeReporterOutput.txt | 0 .../Resources/ProjectMock/.swiftlint.yml | 0 .../ChildConfig/Cycle1/.swiftlint.yml | 0 .../ChildConfig/Cycle2/.swiftlint.yml | 0 .../ProjectMock/ChildConfig/Cycle2/child.yml | 0 .../ChildConfig/Cycle3/Main/.swiftlint.yml | 0 .../ChildConfig/Cycle3/Main/Folder/child2.yml | 0 .../ChildConfig/Cycle3/Main/child1.yml | 0 .../ProjectMock/ChildConfig/Cycle3/child3.yml | 0 .../ProjectMock/ChildConfig/Cycle4/child.yml | 0 .../ProjectMock/ChildConfig/Cycle4/main.yml | 0 .../ChildConfig/Test1/Main/Folder/child3.yml | 0 .../ChildConfig/Test1/Main/child1.yml | 0 .../ChildConfig/Test1/Main/child2.yml | 0 .../ChildConfig/Test1/Main/expected.yml | 0 .../ChildConfig/Test1/Main/main.yml | 0 .../ProjectMock/ChildConfig/Test1/child4.yml | 0 .../ProjectMock/ChildConfig/Test2/child1.yml | 0 .../ProjectMock/ChildConfig/Test2/child2.yml | 0 .../ChildConfig/Test2/expected.yml | 0 .../ProjectMock/ChildConfig/Test2/main.yml | 0 .../Directory.swift/DirectoryLevel1.swift | 0 .../ProjectMock/EmptyFolder/.gitkeep | 0 .../Resources/ProjectMock/Level0.swift | 0 .../Resources/ProjectMock/Level1/Level1.swift | 0 .../ProjectMock/Level1/Level2/.swiftlint.yml | 0 .../ProjectMock/Level1/Level2/Level2.swift | 0 .../Level1/Level2/Level3/.swiftlint.yml | 0 .../Level1/Level2/Level3/Level3.swift | 0 .../Level1/Level2/custom_rules.yml | 0 .../Level1/Level2/custom_rules_disabled.yml | 0 .../Level1/Level2/custom_rules_only.yml | 0 .../Level1/Level2/custom_rules_reconfig.yml | 0 .../NestedConfig/Test/.swiftlint.yml | 0 .../ProjectMock/NestedConfig/Test/Main.swift | 0 .../NestedConfig/Test/Sub/.swiftlint.yml | 0 .../NestedConfig/Test/Sub/Sub.swift | 0 .../ParentConfig/Cycle1/.swiftlint.yml | 0 .../ParentConfig/Cycle2/.swiftlint.yml | 0 .../ParentConfig/Cycle2/parent.yml | 0 .../ParentConfig/Cycle3/.swiftlint.yml | 0 .../ParentConfig/Cycle3/parent.yml | 0 .../ParentConfig/Test1/expected.yml | 0 .../ProjectMock/ParentConfig/Test1/main.yml | 0 .../ParentConfig/Test1/parent1.yml | 0 .../ParentConfig/Test1/parent2.yml | 0 .../ParentConfig/Test2/expected.yml | 0 .../ProjectMock/ParentConfig/Test2/main.yml | 0 .../ParentConfig/Test2/parent1.yml | 0 .../ParentConfig/Test2/parent2.yml | 0 .../ProjectMock/RemoteConfig/Child/child.yml | 0 .../RemoteConfig/Child/expected.yml | 0 .../ProjectMock/RemoteConfig/Child/main.yml | 0 .../RemoteConfig/Cycle/.swiftlint.yml | 0 .../RemoteConfig/LocalRef/.swiftlint.yml | 0 .../RemoteConfig/LocalRef/child1.yml | 0 .../RemoteConfig/LocalRef/child2.yml | 0 .../RemoteConfig/Parent/expected.yml | 0 .../ProjectMock/RemoteConfig/Parent/main.yml | 0 .../RemoteConfig/Parent/parent.yml | 0 .../Resources/ProjectMock/custom.yml | 0 .../ProjectMock/custom_included_excluded.yml | 0 .../Resources/ProjectMock/custom_rules.yml | 0 .../ProjectMock/custom_rules_only.yml | 0 .../Resources/test.txt | 0 .../Resources/test.yml | 0 .../RuleConfigurationDescriptionTests.swift | 2 +- .../RuleConfigurationTests.swift | 0 .../RuleTests.swift | 0 .../RulesTests.swift | 0 .../SourceKitCrashTests.swift | 2 +- .../StringExtensionTests.swift | 0 .../StringViewExtensionTests.swift | 0 .../SwiftLintFileTests.swift | 0 .../SwiftVersionTests.swift | 0 .../YamlParserTests.swift | 0 .../YamlSwiftLintTests.swift | 2 +- Tests/GeneratedTests/GeneratedTests.swift | 2 +- Tests/IntegrationTests/IntegrationTests.swift | 2 +- .../XCTestCase+BundlePath.swift | 20 ----- .../RuleDescription+Examples.swift | 0 Tests/TestHelpers/RuleMock.swift | 19 +++++ .../SwiftLintTestCase.swift | 0 .../TestHelpers.swift | 0 Tests/TestHelpers/TestResources.swift | 14 ++++ 227 files changed, 200 insertions(+), 99 deletions(-) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/AttributesRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/BlanketDisableCommandRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ChildOptionSeverityConfigurationTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/CollectionAlignmentRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ColonRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/CompilerProtocolInitRuleTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ComputedAccessorsOrderRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ConditionalReturnsOnNewlineRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ContainsOverFirstNotNilRuleTests.swift (92%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/CyclomaticComplexityConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/CyclomaticComplexityRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/DeploymentTargetConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/DeploymentTargetRuleTests.swift (95%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/DiscouragedDirectInitRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/DiscouragedObjectLiteralRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/DuplicateImportsRuleTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/EmptyCountRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ExpiringTodoRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ExplicitInitRuleTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ExplicitTypeInterfaceConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ExplicitTypeInterfaceRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FileHeaderRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FileLengthRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FileNameNoSpaceRuleTests.swift (89%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FileNameRuleTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FileTypesOrderRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FunctionBodyLengthRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/FunctionParameterCountRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/GenericTypeNameRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/IdentifierNameRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ImplicitGetterRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ImplicitReturnConfigurationTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ImplicitReturnRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ImplicitlyUnwrappedOptionalConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ImplicitlyUnwrappedOptionalRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/InclusiveLanguageRuleTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/IndentationWidthRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/LineLengthConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/LineLengthRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/MissingDocsRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/MultilineArgumentsRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/MultilineParametersConfigurationTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/NameConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/NestingRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/NoEmptyBlockConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/NumberSeparatorRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/ObjectLiteralRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/OpeningBraceRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/PreferKeyPathRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/PrefixedTopLevelConstantRuleTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/PrivateOverFilePrivateRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/RequiredEnumCaseConfigurationTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/DocumentedType.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileHeaderEmpty.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileNameCaseMismatch.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileNameMatchingComplex.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileNameMatchingSimple.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileNameMismatch.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileHeaderRuleFixtures/FileNameMissing.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameNoSpaceRuleFixtures/File Name.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameNoSpaceRuleFixtures/File+Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameNoSpaceRuleFixtures/File+Test Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameNoSpaceRuleFixtures/File.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/BoolExtension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/BoolExtensionTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/BoolExtensions.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/ExtensionBool+SwiftLint.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/ExtensionBool.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/ExtensionsBool.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/LinuxMain.swift (95%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/MyClass.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/MyStruct.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/MyStructf.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/MyType.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/NSString+Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/Nested.MyType.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/Notification.Name+Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/NotificationName+Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/Notification__Name+Extension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/SLBoolExtension.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/Resources/FileNameRuleFixtures/main.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/StatementPositionRuleTests.swift (94%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/SwitchCaseAlignmentRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TodoRuleTests.swift (95%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TrailingClosureConfigurationTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TrailingClosureRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TrailingCommaRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TrailingWhitespaceRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TypeContentsOrderRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TypeNameRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/TypesafeArrayInitRuleTests.swift (85%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/UnneededOverrideRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/UnusedDeclarationConfigurationTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/UnusedOptionalBindingRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/VerticalWhitespaceRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => BuiltInRulesTests}/XCTSpecificMatcherRuleTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/AccessControlLevelTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/BaselineTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/CodeIndentingRewriterTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/CollectingRuleTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/CommandTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ConfigurationAliasesTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ConfigurationTests+Mock.swift (92%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ConfigurationTests+MultipleConfigs.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ConfigurationTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/CustomRulesTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/DisableAllTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ExampleTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Exports.swift (75%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ExtendedNSStringTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ExtendedStringTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/GlobTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/LineEndingTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/LintOrAnalyzeOptionsTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/LinterCacheTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ModifierOrderTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ParserDiagnosticsTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RegexConfigurationTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RegionTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/ReporterTests.swift (97%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedCSVReporterOutput.csv (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedCheckstyleReporterOutput.xml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedCodeClimateReporterOutput.json (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedEmojiReporterOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedGitHubActionsLoggingReporterOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedGitLabJUnitReporterOutput.xml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedHTMLReporterOutput.html (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedJSONReporterOutput.json (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedJunitReporterOutput.xml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedMarkdownReporterOutput.md (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedRelativePathReporterOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedSARIFReporterOutput.json (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedSonarQubeReporterOutput.json (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedSummaryReporterNoViolationsOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedSummaryReporterOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/CannedXcodeReporterOutput.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle1/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle2/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle2/child.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle3/Main/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle3/Main/Folder/child2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle3/Main/child1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle3/child3.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle4/child.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Cycle4/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/Main/Folder/child3.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/Main/child1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/Main/child2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/Main/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/Main/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test1/child4.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test2/child1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test2/child2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test2/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ChildConfig/Test2/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Directory.swift/DirectoryLevel1.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/EmptyFolder/.gitkeep (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level0.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level1.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/Level2.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/Level3/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/Level3/Level3.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/custom_rules.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/custom_rules_disabled.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/custom_rules_only.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/Level1/Level2/custom_rules_reconfig.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/NestedConfig/Test/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/NestedConfig/Test/Main.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/NestedConfig/Test/Sub/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/NestedConfig/Test/Sub/Sub.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Cycle1/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Cycle2/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Cycle2/parent.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Cycle3/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Cycle3/parent.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test1/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test1/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test1/parent1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test1/parent2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test2/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test2/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test2/parent1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/ParentConfig/Test2/parent2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Child/child.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Child/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Child/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Cycle/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/LocalRef/.swiftlint.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/LocalRef/child1.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/LocalRef/child2.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Parent/expected.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Parent/main.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/RemoteConfig/Parent/parent.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/custom.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/custom_included_excluded.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/custom_rules.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/ProjectMock/custom_rules_only.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/test.txt (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/Resources/test.yml (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RuleConfigurationDescriptionTests.swift (99%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RuleConfigurationTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RuleTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/RulesTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/SourceKitCrashTests.swift (98%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/StringExtensionTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/StringViewExtensionTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/SwiftLintFileTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/SwiftVersionTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/YamlParserTests.swift (100%) rename Tests/{SwiftLintFrameworkTests => FrameworkTests}/YamlSwiftLintTests.swift (96%) delete mode 100644 Tests/SwiftLintFrameworkTests/XCTestCase+BundlePath.swift rename Tests/{SwiftLintTestHelpers => TestHelpers}/RuleDescription+Examples.swift (100%) create mode 100644 Tests/TestHelpers/RuleMock.swift rename Tests/{SwiftLintTestHelpers => TestHelpers}/SwiftLintTestCase.swift (100%) rename Tests/{SwiftLintTestHelpers => TestHelpers}/TestHelpers.swift (100%) create mode 100644 Tests/TestHelpers/TestResources.swift diff --git a/.sourcery/GeneratedTests.stencil b/.sourcery/GeneratedTests.stencil index cd6f14c0fc..8536aec1ab 100644 --- a/.sourcery/GeneratedTests.stencil +++ b/.sourcery/GeneratedTests.stencil @@ -1,6 +1,6 @@ @testable import SwiftLintBuiltInRules @testable import SwiftLintCore -import SwiftLintTestHelpers +import TestHelpers // swiftlint:disable:next blanket_disable_command // swiftlint:disable file_length single_test_class type_name diff --git a/.swiftlint.yml b/.swiftlint.yml index c5f771a23b..ba12d32d8f 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,7 +5,8 @@ included: - Tests - Package.swift excluded: - - Tests/SwiftLintFrameworkTests/Resources + - Tests/BuiltInRulesTests/Resources + - Tests/FrameworkTests/Resources # Enabled/disabled rules analyzer_rules: diff --git a/BUILD b/BUILD index bf7e15a9e6..59db0a9498 100644 --- a/BUILD +++ b/BUILD @@ -194,9 +194,10 @@ cc_library( filegroup( name = "LintInputs", - srcs = glob(["Source/**/*.swift"]) + [ + srcs = glob(["Plugins/**/*.swift", "Source/**/*.swift"]) + [ ".swiftlint.yml", - "//Tests:SwiftLintFrameworkTestsData", + "Package.swift", + "//Tests:TestSources", ], visibility = ["//Tests:__subpackages__"], ) diff --git a/Package.swift b/Package.swift index e7e7ea10b1..d4ac4a774f 100644 --- a/Package.swift +++ b/Package.swift @@ -113,18 +113,18 @@ let package = Package( ), .target(name: "DyldWarningWorkaround"), .target( - name: "SwiftLintTestHelpers", + name: "TestHelpers", dependencies: [ "SwiftLintFramework" ], - path: "Tests/SwiftLintTestHelpers", + path: "Tests/TestHelpers", swiftSettings: swiftFeatures ), .testTarget( - name: "SwiftLintFrameworkTests", + name: "FrameworkTests", dependencies: [ "SwiftLintFramework", - "SwiftLintTestHelpers", + "TestHelpers", "SwiftLintCoreMacros", ], exclude: [ @@ -136,7 +136,7 @@ let package = Package( name: "GeneratedTests", dependencies: [ "SwiftLintFramework", - "SwiftLintTestHelpers", + "TestHelpers", ], swiftSettings: swiftFeatures ), @@ -144,18 +144,30 @@ let package = Package( name: "IntegrationTests", dependencies: [ "SwiftLintFramework", - "SwiftLintTestHelpers", + "TestHelpers", ], exclude: [ "default_rule_configurations.yml" ], swiftSettings: swiftFeatures ), + .testTarget( + name: "BuiltInRulesTests", + dependencies: [ + "SwiftLintBuiltInRules", + "SwiftLintFramework", + "TestHelpers", + ], + exclude: [ + "Resources", + ], + swiftSettings: swiftFeatures + ), .testTarget( name: "ExtraRulesTests", dependencies: [ "SwiftLintFramework", - "SwiftLintTestHelpers", + "TestHelpers", ], swiftSettings: swiftFeatures ), diff --git a/Tests/BUILD b/Tests/BUILD index 2c846a7625..57ef810106 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -55,13 +55,13 @@ swift_test( deps = [":MacroTests.library"], ) -# SwiftLintTestHelpers +# TestHelpers swift_library( - name = "SwiftLintTestHelpers", + name = "TestHelpers", testonly = True, - srcs = glob(["SwiftLintTestHelpers/**/*.swift"]), - module_name = "SwiftLintTestHelpers", + srcs = glob(["TestHelpers/**/*.swift"]), + module_name = "TestHelpers", package_name = "SwiftLint", deps = [ "//:SwiftLintFramework", @@ -69,51 +69,64 @@ swift_library( copts = copts, ) -# SwiftLintFrameworkTests +# BuiltInRulesTests -filegroup( - name = "SwiftLintFrameworkTestsSources", +swift_library( + name = "BuiltInRulesTests.library", + testonly = True, srcs = glob( - ["SwiftLintFrameworkTests/**/*.swift"], + ["BuiltInRulesTests/**/*.swift"], exclude = [ - "SwiftLintFrameworkTests/Resources/**", + "BuiltInRulesTests/Resources/**", # Bazel doesn't support paths with spaces in them - "SwiftLintFrameworkTests/FileNameNoSpaceRuleTests.swift", + "BuiltInRulesTests/FileNameNoSpaceRuleTests.swift", ], ), + module_name = "BuiltInRulesTests", + package_name = "SwiftLint", + deps = [ + ":TestHelpers", + ], + copts = copts, ) -filegroup( - name = "SwiftLintFrameworkTestsData", - srcs = glob( - ["**/**"], +swift_test( + name = "BuiltInRulesTests", + data = glob( + ["BuiltInRulesTests/Resources/**"], # Bazel doesn't support paths with spaces in them - exclude = ["SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/**"], + exclude = ["BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/**"], ), visibility = ["//visibility:public"], + deps = [":BuiltInRulesTests.library"], ) +# FrameworkTests + swift_library( - name = "SwiftLintFrameworkTests.library", + name = "FrameworkTests.library", testonly = True, - srcs = [":SwiftLintFrameworkTestsSources"], - module_name = "SwiftLintFrameworkTests", + srcs = glob( + ["FrameworkTests/**/*.swift"], + exclude = [ + "FrameworkTests/Resources/**", + ], + ), + module_name = "FrameworkTests", package_name = "SwiftLint", deps = [ - ":SwiftLintTestHelpers", + ":TestHelpers", ], copts = copts, ) swift_test( - name = "SwiftLintFrameworkTests", + name = "FrameworkTests", data = glob( - ["SwiftLintFrameworkTests/Resources/**"], - # Bazel doesn't support paths with spaces in them - exclude = ["SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/**"], + ["FrameworkTests/Resources/**"], ), visibility = ["//visibility:public"], - deps = [":SwiftLintFrameworkTests.library"], + deps = [":FrameworkTests.library"], ) # GeneratedTests @@ -125,7 +138,7 @@ swift_library( module_name = "GeneratedTests", package_name = "SwiftLint", deps = [ - ":SwiftLintTestHelpers", + ":TestHelpers", ], copts = copts, ) @@ -138,6 +151,15 @@ swift_test( # IntegrationTests +filegroup( + name = "TestSources", + srcs = glob( + ["**/*.swift"], + exclude = ["**/Resources/**"], + ), + visibility = ["//visibility:public"], +) + swift_library( name = "IntegrationTests.library", testonly = True, @@ -145,7 +167,7 @@ swift_library( module_name = "IntegrationTests", package_name = "SwiftLint", deps = [ - ":SwiftLintTestHelpers", + ":TestHelpers", ], copts = copts, ) @@ -184,7 +206,7 @@ swift_library( module_name = "ExtraRulesTests", package_name = "SwiftLint", deps = [ - ":SwiftLintTestHelpers", + ":TestHelpers", ], ) diff --git a/Tests/SwiftLintFrameworkTests/AttributesRuleTests.swift b/Tests/BuiltInRulesTests/AttributesRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/AttributesRuleTests.swift rename to Tests/BuiltInRulesTests/AttributesRuleTests.swift index 2a0f5444ee..ec8a993573 100644 --- a/Tests/SwiftLintFrameworkTests/AttributesRuleTests.swift +++ b/Tests/BuiltInRulesTests/AttributesRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class AttributesRuleTests: SwiftLintTestCase { func testAttributesWithAlwaysOnSameLine() { diff --git a/Tests/SwiftLintFrameworkTests/BlanketDisableCommandRuleTests.swift b/Tests/BuiltInRulesTests/BlanketDisableCommandRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/BlanketDisableCommandRuleTests.swift rename to Tests/BuiltInRulesTests/BlanketDisableCommandRuleTests.swift index 9540c085c1..061f7a637e 100644 --- a/Tests/SwiftLintFrameworkTests/BlanketDisableCommandRuleTests.swift +++ b/Tests/BuiltInRulesTests/BlanketDisableCommandRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class BlanketDisableCommandRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ChildOptionSeverityConfigurationTests.swift b/Tests/BuiltInRulesTests/ChildOptionSeverityConfigurationTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/ChildOptionSeverityConfigurationTests.swift rename to Tests/BuiltInRulesTests/ChildOptionSeverityConfigurationTests.swift index 10d4cd4d0c..71d82b74e9 100644 --- a/Tests/SwiftLintFrameworkTests/ChildOptionSeverityConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ChildOptionSeverityConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ChildOptionSeverityConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/CollectionAlignmentRuleTests.swift b/Tests/BuiltInRulesTests/CollectionAlignmentRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/CollectionAlignmentRuleTests.swift rename to Tests/BuiltInRulesTests/CollectionAlignmentRuleTests.swift index 364589bc88..13b63a3b82 100644 --- a/Tests/SwiftLintFrameworkTests/CollectionAlignmentRuleTests.swift +++ b/Tests/BuiltInRulesTests/CollectionAlignmentRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class CollectionAlignmentRuleTests: SwiftLintTestCase { func testCollectionAlignmentWithAlignLeft() { diff --git a/Tests/SwiftLintFrameworkTests/ColonRuleTests.swift b/Tests/BuiltInRulesTests/ColonRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ColonRuleTests.swift rename to Tests/BuiltInRulesTests/ColonRuleTests.swift index 095214241e..cc3c45f7cf 100644 --- a/Tests/SwiftLintFrameworkTests/ColonRuleTests.swift +++ b/Tests/BuiltInRulesTests/ColonRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ColonRuleTests: SwiftLintTestCase { func testColonWithFlexibleRightSpace() { diff --git a/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift b/Tests/BuiltInRulesTests/CompilerProtocolInitRuleTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift rename to Tests/BuiltInRulesTests/CompilerProtocolInitRuleTests.swift index 68460fd73b..27ddecd07f 100644 --- a/Tests/SwiftLintFrameworkTests/CompilerProtocolInitRuleTests.swift +++ b/Tests/BuiltInRulesTests/CompilerProtocolInitRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class CompilerProtocolInitRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift b/Tests/BuiltInRulesTests/ComputedAccessorsOrderRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift rename to Tests/BuiltInRulesTests/ComputedAccessorsOrderRuleTests.swift index 1066d61010..878814f0b3 100644 --- a/Tests/SwiftLintFrameworkTests/ComputedAccessorsOrderRuleTests.swift +++ b/Tests/BuiltInRulesTests/ComputedAccessorsOrderRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ComputedAccessorsOrderRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ConditionalReturnsOnNewlineRuleTests.swift b/Tests/BuiltInRulesTests/ConditionalReturnsOnNewlineRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/ConditionalReturnsOnNewlineRuleTests.swift rename to Tests/BuiltInRulesTests/ConditionalReturnsOnNewlineRuleTests.swift index e2ffbbbc1f..96a31dbd7e 100644 --- a/Tests/SwiftLintFrameworkTests/ConditionalReturnsOnNewlineRuleTests.swift +++ b/Tests/BuiltInRulesTests/ConditionalReturnsOnNewlineRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ConditionalReturnsOnNewlineRuleTests: SwiftLintTestCase { func testConditionalReturnsOnNewlineWithIfOnly() { diff --git a/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift b/Tests/BuiltInRulesTests/ContainsOverFirstNotNilRuleTests.swift similarity index 92% rename from Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift rename to Tests/BuiltInRulesTests/ContainsOverFirstNotNilRuleTests.swift index 83c162fba3..c2657af060 100644 --- a/Tests/SwiftLintFrameworkTests/ContainsOverFirstNotNilRuleTests.swift +++ b/Tests/BuiltInRulesTests/ContainsOverFirstNotNilRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ContainsOverFirstNotNilRuleTests: SwiftLintTestCase { @@ -25,6 +26,6 @@ final class ContainsOverFirstNotNilRuleTests: SwiftLintTestCase { return [] } - return SwiftLintFrameworkTests.violations(example, config: config) + return TestHelpers.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift b/Tests/BuiltInRulesTests/CyclomaticComplexityConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift rename to Tests/BuiltInRulesTests/CyclomaticComplexityConfigurationTests.swift index eb9638a82f..d81f08b69b 100644 --- a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/CyclomaticComplexityConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class CyclomaticComplexityConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityRuleTests.swift b/Tests/BuiltInRulesTests/CyclomaticComplexityRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/CyclomaticComplexityRuleTests.swift rename to Tests/BuiltInRulesTests/CyclomaticComplexityRuleTests.swift index 8dab89a1b4..4b6e28bb64 100644 --- a/Tests/SwiftLintFrameworkTests/CyclomaticComplexityRuleTests.swift +++ b/Tests/BuiltInRulesTests/CyclomaticComplexityRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class CyclomaticComplexityRuleTests: SwiftLintTestCase { private lazy var complexSwitchExample: Example = { diff --git a/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift b/Tests/BuiltInRulesTests/DeploymentTargetConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift rename to Tests/BuiltInRulesTests/DeploymentTargetConfigurationTests.swift index f9edd35491..f5ee0e659b 100644 --- a/Tests/SwiftLintFrameworkTests/DeploymentTargetConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/DeploymentTargetConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class DeploymentTargetConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift b/Tests/BuiltInRulesTests/DeploymentTargetRuleTests.swift similarity index 95% rename from Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift rename to Tests/BuiltInRulesTests/DeploymentTargetRuleTests.swift index b101b89b0c..15c81e378a 100644 --- a/Tests/SwiftLintFrameworkTests/DeploymentTargetRuleTests.swift +++ b/Tests/BuiltInRulesTests/DeploymentTargetRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class DeploymentTargetRuleTests: SwiftLintTestCase { @@ -39,6 +40,6 @@ final class DeploymentTargetRuleTests: SwiftLintTestCase { return [] } - return SwiftLintFrameworkTests.violations(example, config: config) + return TestHelpers.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/DiscouragedDirectInitRuleTests.swift b/Tests/BuiltInRulesTests/DiscouragedDirectInitRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/DiscouragedDirectInitRuleTests.swift rename to Tests/BuiltInRulesTests/DiscouragedDirectInitRuleTests.swift index 74f0f4fd57..35897dec9c 100644 --- a/Tests/SwiftLintFrameworkTests/DiscouragedDirectInitRuleTests.swift +++ b/Tests/BuiltInRulesTests/DiscouragedDirectInitRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class DiscouragedDirectInitRuleTests: SwiftLintTestCase { private let baseDescription = DiscouragedDirectInitRule.description diff --git a/Tests/SwiftLintFrameworkTests/DiscouragedObjectLiteralRuleTests.swift b/Tests/BuiltInRulesTests/DiscouragedObjectLiteralRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/DiscouragedObjectLiteralRuleTests.swift rename to Tests/BuiltInRulesTests/DiscouragedObjectLiteralRuleTests.swift index 1a2f17e04e..fe90df77e4 100644 --- a/Tests/SwiftLintFrameworkTests/DiscouragedObjectLiteralRuleTests.swift +++ b/Tests/BuiltInRulesTests/DiscouragedObjectLiteralRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class DiscouragedObjectLiteralRuleTests: SwiftLintTestCase { func testWithImageLiteral() { diff --git a/Tests/SwiftLintFrameworkTests/DuplicateImportsRuleTests.swift b/Tests/BuiltInRulesTests/DuplicateImportsRuleTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/DuplicateImportsRuleTests.swift rename to Tests/BuiltInRulesTests/DuplicateImportsRuleTests.swift diff --git a/Tests/SwiftLintFrameworkTests/EmptyCountRuleTests.swift b/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/EmptyCountRuleTests.swift rename to Tests/BuiltInRulesTests/EmptyCountRuleTests.swift index 7dddfe4f23..7df9e49108 100644 --- a/Tests/SwiftLintFrameworkTests/EmptyCountRuleTests.swift +++ b/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class EmptyCountRuleTests: SwiftLintTestCase { func testEmptyCountWithOnlyAfterDot() { diff --git a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift b/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift rename to Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift index bb723d5321..b0dc3015f1 100644 --- a/Tests/SwiftLintFrameworkTests/ExpiringTodoRuleTests.swift +++ b/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift @@ -1,5 +1,6 @@ @testable import SwiftLintBuiltInRules import SwiftLintFramework +import TestHelpers import XCTest final class ExpiringTodoRuleTests: SwiftLintTestCase { @@ -142,7 +143,7 @@ final class ExpiringTodoRuleTests: SwiftLintTestCase { } private func violations(_ example: Example) -> [StyleViolation] { - SwiftLintFrameworkTests.violations(example, config: config) + TestHelpers.violations(example, config: config) } private func dateString(for status: ExpiringTodoRule.ExpiryViolationLevel) -> String { diff --git a/Tests/SwiftLintFrameworkTests/ExplicitInitRuleTests.swift b/Tests/BuiltInRulesTests/ExplicitInitRuleTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/ExplicitInitRuleTests.swift rename to Tests/BuiltInRulesTests/ExplicitInitRuleTests.swift index 65dfee4653..407efbe3bc 100644 --- a/Tests/SwiftLintFrameworkTests/ExplicitInitRuleTests.swift +++ b/Tests/BuiltInRulesTests/ExplicitInitRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ExplicitInitRuleTests: SwiftLintTestCase { func testIncludeBareInit() { diff --git a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift rename to Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift index df4d43731c..ee1d9b7a60 100644 --- a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift @@ -1,5 +1,6 @@ @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +import TestHelpers import XCTest final class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceRuleTests.swift b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceRuleTests.swift rename to Tests/BuiltInRulesTests/ExplicitTypeInterfaceRuleTests.swift index 37d079aa3a..99c9120ec3 100644 --- a/Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceRuleTests.swift +++ b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ExplicitTypeInterfaceRuleTests: SwiftLintTestCase { func testLocalVars() { diff --git a/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift b/Tests/BuiltInRulesTests/FileHeaderRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift rename to Tests/BuiltInRulesTests/FileHeaderRuleTests.swift index 8867afcf4a..82b56cdfea 100644 --- a/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift +++ b/Tests/BuiltInRulesTests/FileHeaderRuleTests.swift @@ -1,7 +1,8 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest -private let fixturesDirectory = "\(TestResources.path)/FileHeaderRuleFixtures" +private let fixturesDirectory = "\(TestResources.path())/FileHeaderRuleFixtures" final class FileHeaderRuleTests: SwiftLintTestCase { private func validate(fileName: String, using configuration: Any) throws -> [StyleViolation] { diff --git a/Tests/SwiftLintFrameworkTests/FileLengthRuleTests.swift b/Tests/BuiltInRulesTests/FileLengthRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/FileLengthRuleTests.swift rename to Tests/BuiltInRulesTests/FileLengthRuleTests.swift index 6b629889bf..490377caac 100644 --- a/Tests/SwiftLintFrameworkTests/FileLengthRuleTests.swift +++ b/Tests/BuiltInRulesTests/FileLengthRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class FileLengthRuleTests: SwiftLintTestCase { func testFileLengthWithDefaultConfiguration() { diff --git a/Tests/SwiftLintFrameworkTests/FileNameNoSpaceRuleTests.swift b/Tests/BuiltInRulesTests/FileNameNoSpaceRuleTests.swift similarity index 89% rename from Tests/SwiftLintFrameworkTests/FileNameNoSpaceRuleTests.swift rename to Tests/BuiltInRulesTests/FileNameNoSpaceRuleTests.swift index 4aee8dcc7c..9c0b3a1d01 100644 --- a/Tests/SwiftLintFrameworkTests/FileNameNoSpaceRuleTests.swift +++ b/Tests/BuiltInRulesTests/FileNameNoSpaceRuleTests.swift @@ -1,10 +1,9 @@ import SourceKittenFramework @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest -private let fixturesDirectory = #filePath.bridge() - .deletingLastPathComponent.bridge() - .appendingPathComponent("Resources/FileNameNoSpaceRuleFixtures") +private let fixturesDirectory = "\(TestResources.path())/FileNameNoSpaceRuleFixtures" final class FileNameNoSpaceRuleTests: SwiftLintTestCase { private func validate(fileName: String, excludedOverride: [String]? = nil) throws -> [StyleViolation] { diff --git a/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift b/Tests/BuiltInRulesTests/FileNameRuleTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift rename to Tests/BuiltInRulesTests/FileNameRuleTests.swift index 4e3a2923fa..147112846b 100644 --- a/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift +++ b/Tests/BuiltInRulesTests/FileNameRuleTests.swift @@ -1,7 +1,8 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest -private let fixturesDirectory = "\(TestResources.path)/FileNameRuleFixtures" +private let fixturesDirectory = "\(TestResources.path())/FileNameRuleFixtures" final class FileNameRuleTests: SwiftLintTestCase { private func validate(fileName: String, diff --git a/Tests/SwiftLintFrameworkTests/FileTypesOrderRuleTests.swift b/Tests/BuiltInRulesTests/FileTypesOrderRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/FileTypesOrderRuleTests.swift rename to Tests/BuiltInRulesTests/FileTypesOrderRuleTests.swift index e9a6f8c93d..4632cd32f7 100644 --- a/Tests/SwiftLintFrameworkTests/FileTypesOrderRuleTests.swift +++ b/Tests/BuiltInRulesTests/FileTypesOrderRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class FileTypesOrderRuleTests: SwiftLintTestCase { // swiftlint:disable:next function_body_length diff --git a/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift b/Tests/BuiltInRulesTests/FunctionBodyLengthRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift rename to Tests/BuiltInRulesTests/FunctionBodyLengthRuleTests.swift index d849b2996d..f8acfbaa2e 100644 --- a/Tests/SwiftLintFrameworkTests/FunctionBodyLengthRuleTests.swift +++ b/Tests/BuiltInRulesTests/FunctionBodyLengthRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest private func funcWithBody(_ body: String, @@ -102,6 +103,6 @@ final class FunctionBodyLengthRuleTests: SwiftLintTestCase { private func violations(_ example: Example, configuration: Any? = nil) -> [StyleViolation] { let config = makeConfig(configuration, FunctionBodyLengthRule.identifier)! - return SwiftLintFrameworkTests.violations(example, config: config) + return TestHelpers.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift b/Tests/BuiltInRulesTests/FunctionParameterCountRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift rename to Tests/BuiltInRulesTests/FunctionParameterCountRuleTests.swift index c8f3e3ebf3..0c9b24d21c 100644 --- a/Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift +++ b/Tests/BuiltInRulesTests/FunctionParameterCountRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers private func funcWithParameters(_ parameters: String, violates: Bool = false, diff --git a/Tests/SwiftLintFrameworkTests/GenericTypeNameRuleTests.swift b/Tests/BuiltInRulesTests/GenericTypeNameRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/GenericTypeNameRuleTests.swift rename to Tests/BuiltInRulesTests/GenericTypeNameRuleTests.swift index 8473e98a8f..bfac1930fc 100644 --- a/Tests/SwiftLintFrameworkTests/GenericTypeNameRuleTests.swift +++ b/Tests/BuiltInRulesTests/GenericTypeNameRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class GenericTypeNameRuleTests: SwiftLintTestCase { func testGenericTypeNameWithExcluded() { diff --git a/Tests/SwiftLintFrameworkTests/IdentifierNameRuleTests.swift b/Tests/BuiltInRulesTests/IdentifierNameRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/IdentifierNameRuleTests.swift rename to Tests/BuiltInRulesTests/IdentifierNameRuleTests.swift index 324d4d197e..48bba3ebcb 100644 --- a/Tests/SwiftLintFrameworkTests/IdentifierNameRuleTests.swift +++ b/Tests/BuiltInRulesTests/IdentifierNameRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class IdentifierNameRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift b/Tests/BuiltInRulesTests/ImplicitGetterRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift rename to Tests/BuiltInRulesTests/ImplicitGetterRuleTests.swift index 0905bf01f9..105b55c98c 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitGetterRuleTests.swift +++ b/Tests/BuiltInRulesTests/ImplicitGetterRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ImplicitGetterRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift b/Tests/BuiltInRulesTests/ImplicitReturnConfigurationTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift rename to Tests/BuiltInRulesTests/ImplicitReturnConfigurationTests.swift index b19dd28e02..f3d2e82a5c 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitReturnConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ImplicitReturnConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ImplicitReturnConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ImplicitReturnRuleTests.swift b/Tests/BuiltInRulesTests/ImplicitReturnRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ImplicitReturnRuleTests.swift rename to Tests/BuiltInRulesTests/ImplicitReturnRuleTests.swift index 6aa4036f22..97f480f59e 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitReturnRuleTests.swift +++ b/Tests/BuiltInRulesTests/ImplicitReturnRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ImplicitReturnRuleTests: SwiftLintTestCase { func testOnlyClosureKindIncluded() { diff --git a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift b/Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift rename to Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift index 6ba01c625c..4e2b716b19 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest // swiftlint:disable:next type_name diff --git a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift b/Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift rename to Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalRuleTests.swift index 4d677b5bc2..e840f3da0d 100644 --- a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift +++ b/Tests/BuiltInRulesTests/ImplicitlyUnwrappedOptionalRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class ImplicitlyUnwrappedOptionalRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/InclusiveLanguageRuleTests.swift b/Tests/BuiltInRulesTests/InclusiveLanguageRuleTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/InclusiveLanguageRuleTests.swift rename to Tests/BuiltInRulesTests/InclusiveLanguageRuleTests.swift index a4eecf6901..a2b291fad1 100644 --- a/Tests/SwiftLintFrameworkTests/InclusiveLanguageRuleTests.swift +++ b/Tests/BuiltInRulesTests/InclusiveLanguageRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class InclusiveLanguageRuleTests: SwiftLintTestCase { func testNonTriggeringExamplesWithNonDefaultConfig() { diff --git a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift rename to Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift index 4946e63adc..c8ae98abab 100644 --- a/Tests/SwiftLintFrameworkTests/IndentationWidthRuleTests.swift +++ b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift @@ -1,6 +1,6 @@ @testable import SwiftLintBuiltInRules @testable import SwiftLintCore -import SwiftLintTestHelpers +import TestHelpers import XCTest final class IndentationWidthRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift b/Tests/BuiltInRulesTests/LineLengthConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift rename to Tests/BuiltInRulesTests/LineLengthConfigurationTests.swift index 55360aabe8..c617bdc4fa 100644 --- a/Tests/SwiftLintFrameworkTests/LineLengthConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/LineLengthConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class LineLengthConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/LineLengthRuleTests.swift b/Tests/BuiltInRulesTests/LineLengthRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/LineLengthRuleTests.swift rename to Tests/BuiltInRulesTests/LineLengthRuleTests.swift index 497ce3b6af..463d49ab87 100644 --- a/Tests/SwiftLintFrameworkTests/LineLengthRuleTests.swift +++ b/Tests/BuiltInRulesTests/LineLengthRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class LineLengthRuleTests: SwiftLintTestCase { private let longFunctionDeclarations = [ diff --git a/Tests/SwiftLintFrameworkTests/MissingDocsRuleTests.swift b/Tests/BuiltInRulesTests/MissingDocsRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/MissingDocsRuleTests.swift rename to Tests/BuiltInRulesTests/MissingDocsRuleTests.swift index 531373d1bc..8255c1693d 100644 --- a/Tests/SwiftLintFrameworkTests/MissingDocsRuleTests.swift +++ b/Tests/BuiltInRulesTests/MissingDocsRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class MissingDocsRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/MultilineArgumentsRuleTests.swift b/Tests/BuiltInRulesTests/MultilineArgumentsRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/MultilineArgumentsRuleTests.swift rename to Tests/BuiltInRulesTests/MultilineArgumentsRuleTests.swift index 2f66f76dbc..fe0a9faf44 100644 --- a/Tests/SwiftLintFrameworkTests/MultilineArgumentsRuleTests.swift +++ b/Tests/BuiltInRulesTests/MultilineArgumentsRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class MultilineArgumentsRuleTests: SwiftLintTestCase { func testMultilineArgumentsWithWithNextLine() { diff --git a/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift rename to Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift index ecea8d2c40..12049483d0 100644 --- a/Tests/SwiftLintFrameworkTests/MultilineParametersConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift @@ -1,5 +1,6 @@ @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +import TestHelpers import XCTest final class MultilineParametersConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift b/Tests/BuiltInRulesTests/NameConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift rename to Tests/BuiltInRulesTests/NameConfigurationTests.swift index fc39b2bf29..b9ca0449ce 100644 --- a/Tests/SwiftLintFrameworkTests/NameConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/NameConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class NameConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift b/Tests/BuiltInRulesTests/NestingRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/NestingRuleTests.swift rename to Tests/BuiltInRulesTests/NestingRuleTests.swift index 435ca660be..b41082edb4 100644 --- a/Tests/SwiftLintFrameworkTests/NestingRuleTests.swift +++ b/Tests/BuiltInRulesTests/NestingRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers // swiftlint:disable file_length diff --git a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift rename to Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift index 9a88de66aa..96f287145d 100644 --- a/Tests/SwiftLintFrameworkTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift @@ -1,5 +1,6 @@ @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +import TestHelpers import XCTest final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/NumberSeparatorRuleTests.swift b/Tests/BuiltInRulesTests/NumberSeparatorRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/NumberSeparatorRuleTests.swift rename to Tests/BuiltInRulesTests/NumberSeparatorRuleTests.swift index e80d93b639..362e55ce4f 100644 --- a/Tests/SwiftLintFrameworkTests/NumberSeparatorRuleTests.swift +++ b/Tests/BuiltInRulesTests/NumberSeparatorRuleTests.swift @@ -1,5 +1,6 @@ @testable import SwiftLintBuiltInRules import SwiftParser +import TestHelpers import XCTest final class NumberSeparatorRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/ObjectLiteralRuleTests.swift b/Tests/BuiltInRulesTests/ObjectLiteralRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/ObjectLiteralRuleTests.swift rename to Tests/BuiltInRulesTests/ObjectLiteralRuleTests.swift index bfdb9ba375..988b8a9a93 100644 --- a/Tests/SwiftLintFrameworkTests/ObjectLiteralRuleTests.swift +++ b/Tests/BuiltInRulesTests/ObjectLiteralRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class ObjectLiteralRuleTests: SwiftLintTestCase { // MARK: - Instance Properties diff --git a/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift b/Tests/BuiltInRulesTests/OpeningBraceRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift rename to Tests/BuiltInRulesTests/OpeningBraceRuleTests.swift index 445b2afd44..fe04b07fda 100644 --- a/Tests/SwiftLintFrameworkTests/OpeningBraceRuleTests.swift +++ b/Tests/BuiltInRulesTests/OpeningBraceRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class OpeningBraceRuleTests: SwiftLintTestCase { func testDefaultNonTriggeringExamplesWithMultilineOptionsTrue() { diff --git a/Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift b/Tests/BuiltInRulesTests/PreferKeyPathRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift rename to Tests/BuiltInRulesTests/PreferKeyPathRuleTests.swift index 4b99289fc0..830420df5d 100644 --- a/Tests/SwiftLintFrameworkTests/PreferKeyPathRuleTests.swift +++ b/Tests/BuiltInRulesTests/PreferKeyPathRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class PreferKeyPathRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/PrefixedTopLevelConstantRuleTests.swift b/Tests/BuiltInRulesTests/PrefixedTopLevelConstantRuleTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/PrefixedTopLevelConstantRuleTests.swift rename to Tests/BuiltInRulesTests/PrefixedTopLevelConstantRuleTests.swift index d2102a6db2..bbe836d3a7 100644 --- a/Tests/SwiftLintFrameworkTests/PrefixedTopLevelConstantRuleTests.swift +++ b/Tests/BuiltInRulesTests/PrefixedTopLevelConstantRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class PrefixedTopLevelConstantRuleTests: SwiftLintTestCase { func testPrivateOnly() { diff --git a/Tests/SwiftLintFrameworkTests/PrivateOverFilePrivateRuleTests.swift b/Tests/BuiltInRulesTests/PrivateOverFilePrivateRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/PrivateOverFilePrivateRuleTests.swift rename to Tests/BuiltInRulesTests/PrivateOverFilePrivateRuleTests.swift index 56a97267ef..baa77f7ee0 100644 --- a/Tests/SwiftLintFrameworkTests/PrivateOverFilePrivateRuleTests.swift +++ b/Tests/BuiltInRulesTests/PrivateOverFilePrivateRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class PrivateOverFilePrivateRuleTests: SwiftLintTestCase { func testPrivateOverFilePrivateValidatingExtensions() { diff --git a/Tests/SwiftLintFrameworkTests/RequiredEnumCaseConfigurationTests.swift b/Tests/BuiltInRulesTests/RequiredEnumCaseConfigurationTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/RequiredEnumCaseConfigurationTests.swift rename to Tests/BuiltInRulesTests/RequiredEnumCaseConfigurationTests.swift index 1ec83cbcde..bad8e2e969 100644 --- a/Tests/SwiftLintFrameworkTests/RequiredEnumCaseConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/RequiredEnumCaseConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class RequiredEnumCaseConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/DocumentedType.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/DocumentedType.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/DocumentedType.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/DocumentedType.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileHeaderEmpty.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileHeaderEmpty.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileHeaderEmpty.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileHeaderEmpty.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameCaseMismatch.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameCaseMismatch.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameCaseMismatch.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameCaseMismatch.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMatchingComplex.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMatchingComplex.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMatchingComplex.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMatchingComplex.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMatchingSimple.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMatchingSimple.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMatchingSimple.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMatchingSimple.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMismatch.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMismatch.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMismatch.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMismatch.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMissing.swift b/Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMissing.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileHeaderRuleFixtures/FileNameMissing.swift rename to Tests/BuiltInRulesTests/Resources/FileHeaderRuleFixtures/FileNameMissing.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File Name.swift b/Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File Name.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File Name.swift rename to Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File Name.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File+Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File+Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File+Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File+Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File+Test Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File+Test Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File+Test Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File+Test Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File.swift b/Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameNoSpaceRuleFixtures/File.swift rename to Tests/BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/File.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtensionTests.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtensionTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtensionTests.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtensionTests.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtensions.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtensions.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/BoolExtensions.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/BoolExtensions.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionBool+SwiftLint.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionBool+SwiftLint.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionBool+SwiftLint.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionBool+SwiftLint.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionBool.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionBool.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionBool.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionBool.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionsBool.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionsBool.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/ExtensionsBool.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/ExtensionsBool.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/LinuxMain.swift similarity index 95% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/LinuxMain.swift index 96fc6d1799..48d37ac814 100644 --- a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift +++ b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/LinuxMain.swift @@ -5,6 +5,6 @@ extension AttributesRuleTests { static var allTests: [(String, (AttributesRuleTests) -> () throws -> Void)] = [ ("testAttributesWithDefaultConfiguration", testAttributesWithDefaultConfiguration), ("testAttributesWithAlwaysOnSameLine", testAttributesWithAlwaysOnSameLine), - ("testAttributesWithAlwaysOnLineAbove", testAttributesWithAlwaysOnLineAbove) + ("testAttributesWithAlwaysOnLineAbove", testAttributesWithAlwaysOnLineAbove), ] } diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Multiple.Levels.Deeply.Nested.MyType.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyClass.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyClass.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyClass.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyClass.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStruct.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyStruct.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStruct.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyStruct.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStructf.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyStructf.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStructf.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyStructf.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyType.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyType.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/MyType.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NSString+Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/NSString+Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NSString+Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/NSString+Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Nested.MyType.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Nested.MyType.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Nested.MyType.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Notification.Name+Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Notification.Name+Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Notification.Name+Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Notification.Name+Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NotificationName+Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/NotificationName+Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NotificationName+Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/NotificationName+Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Notification__Name+Extension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Notification__Name+Extension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/Notification__Name+Extension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/Notification__Name+Extension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/SLBoolExtension.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/SLBoolExtension.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/SLBoolExtension.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/SLBoolExtension.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/main.swift b/Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/main.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/main.swift rename to Tests/BuiltInRulesTests/Resources/FileNameRuleFixtures/main.swift diff --git a/Tests/SwiftLintFrameworkTests/StatementPositionRuleTests.swift b/Tests/BuiltInRulesTests/StatementPositionRuleTests.swift similarity index 94% rename from Tests/SwiftLintFrameworkTests/StatementPositionRuleTests.swift rename to Tests/BuiltInRulesTests/StatementPositionRuleTests.swift index b776b05302..ab4b81f353 100644 --- a/Tests/SwiftLintFrameworkTests/StatementPositionRuleTests.swift +++ b/Tests/BuiltInRulesTests/StatementPositionRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class StatementPositionRuleTests: SwiftLintTestCase { func testStatementPositionUncuddled() { diff --git a/Tests/SwiftLintFrameworkTests/SwitchCaseAlignmentRuleTests.swift b/Tests/BuiltInRulesTests/SwitchCaseAlignmentRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/SwitchCaseAlignmentRuleTests.swift rename to Tests/BuiltInRulesTests/SwitchCaseAlignmentRuleTests.swift index 5ec28069b9..9bada421fa 100644 --- a/Tests/SwiftLintFrameworkTests/SwitchCaseAlignmentRuleTests.swift +++ b/Tests/BuiltInRulesTests/SwitchCaseAlignmentRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class SwitchCaseAlignmentRuleTests: SwiftLintTestCase { func testSwitchCaseAlignmentWithoutIndentedCases() { diff --git a/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift b/Tests/BuiltInRulesTests/TodoRuleTests.swift similarity index 95% rename from Tests/SwiftLintFrameworkTests/TodoRuleTests.swift rename to Tests/BuiltInRulesTests/TodoRuleTests.swift index 8130bc54cc..aebfec9dbe 100644 --- a/Tests/SwiftLintFrameworkTests/TodoRuleTests.swift +++ b/Tests/BuiltInRulesTests/TodoRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class TodoRuleTests: SwiftLintTestCase { @@ -42,6 +43,6 @@ final class TodoRuleTests: SwiftLintTestCase { private func violations(_ example: Example, config: Any? = nil) -> [StyleViolation] { let config = makeConfig(config, TodoRule.identifier)! - return SwiftLintFrameworkTests.violations(example, config: config) + return TestHelpers.violations(example, config: config) } } diff --git a/Tests/SwiftLintFrameworkTests/TrailingClosureConfigurationTests.swift b/Tests/BuiltInRulesTests/TrailingClosureConfigurationTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/TrailingClosureConfigurationTests.swift rename to Tests/BuiltInRulesTests/TrailingClosureConfigurationTests.swift index 51ffc479ce..0baef0beb9 100644 --- a/Tests/SwiftLintFrameworkTests/TrailingClosureConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/TrailingClosureConfigurationTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class TrailingClosureConfigurationTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/TrailingClosureRuleTests.swift b/Tests/BuiltInRulesTests/TrailingClosureRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/TrailingClosureRuleTests.swift rename to Tests/BuiltInRulesTests/TrailingClosureRuleTests.swift index c50697287d..4ffd56a096 100644 --- a/Tests/SwiftLintFrameworkTests/TrailingClosureRuleTests.swift +++ b/Tests/BuiltInRulesTests/TrailingClosureRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class TrailingClosureRuleTests: SwiftLintTestCase { func testWithOnlySingleMutedParameterEnabled() { diff --git a/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift b/Tests/BuiltInRulesTests/TrailingCommaRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift rename to Tests/BuiltInRulesTests/TrailingCommaRuleTests.swift index e7b4a8f361..a4aa896a31 100644 --- a/Tests/SwiftLintFrameworkTests/TrailingCommaRuleTests.swift +++ b/Tests/BuiltInRulesTests/TrailingCommaRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class TrailingCommaRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/TrailingWhitespaceRuleTests.swift b/Tests/BuiltInRulesTests/TrailingWhitespaceRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/TrailingWhitespaceRuleTests.swift rename to Tests/BuiltInRulesTests/TrailingWhitespaceRuleTests.swift index 6de8111141..e79c400467 100644 --- a/Tests/SwiftLintFrameworkTests/TrailingWhitespaceRuleTests.swift +++ b/Tests/BuiltInRulesTests/TrailingWhitespaceRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class TrailingWhitespaceRuleTests: SwiftLintTestCase { func testWithIgnoresEmptyLinesEnabled() { diff --git a/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift b/Tests/BuiltInRulesTests/TypeContentsOrderRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift rename to Tests/BuiltInRulesTests/TypeContentsOrderRuleTests.swift index 808e3bb41d..8f4b0bd1c0 100644 --- a/Tests/SwiftLintFrameworkTests/TypeContentsOrderRuleTests.swift +++ b/Tests/BuiltInRulesTests/TypeContentsOrderRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class TypeContentsOrderRuleTests: SwiftLintTestCase { // swiftlint:disable:next function_body_length diff --git a/Tests/SwiftLintFrameworkTests/TypeNameRuleTests.swift b/Tests/BuiltInRulesTests/TypeNameRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/TypeNameRuleTests.swift rename to Tests/BuiltInRulesTests/TypeNameRuleTests.swift index 2c8819a5ec..4cebbfa871 100644 --- a/Tests/SwiftLintFrameworkTests/TypeNameRuleTests.swift +++ b/Tests/BuiltInRulesTests/TypeNameRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class TypeNameRuleTests: SwiftLintTestCase { func testTypeNameWithExcluded() { diff --git a/Tests/SwiftLintFrameworkTests/TypesafeArrayInitRuleTests.swift b/Tests/BuiltInRulesTests/TypesafeArrayInitRuleTests.swift similarity index 85% rename from Tests/SwiftLintFrameworkTests/TypesafeArrayInitRuleTests.swift rename to Tests/BuiltInRulesTests/TypesafeArrayInitRuleTests.swift index 59d0769491..792f8090db 100644 --- a/Tests/SwiftLintFrameworkTests/TypesafeArrayInitRuleTests.swift +++ b/Tests/BuiltInRulesTests/TypesafeArrayInitRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class TypesafeArrayInitRuleTests: SwiftLintTestCase { @@ -12,7 +13,7 @@ final class TypesafeArrayInitRuleTests: SwiftLintTestCase { XCTFail("Failed to create configuration") return } - let violations = SwiftLintFrameworkTests.violations(triggeringExample, config: config, requiresFileOnDisk: true) + let violations = violations(triggeringExample, config: config, requiresFileOnDisk: true) XCTAssertGreaterThanOrEqual(violations.count, 1) XCTAssertEqual(violations.first?.ruleIdentifier, baseDescription.identifier) } diff --git a/Tests/SwiftLintFrameworkTests/UnneededOverrideRuleTests.swift b/Tests/BuiltInRulesTests/UnneededOverrideRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/UnneededOverrideRuleTests.swift rename to Tests/BuiltInRulesTests/UnneededOverrideRuleTests.swift index 8ee55ec026..f32bea1598 100644 --- a/Tests/SwiftLintFrameworkTests/UnneededOverrideRuleTests.swift +++ b/Tests/BuiltInRulesTests/UnneededOverrideRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class UnneededOverrideRuleTests: SwiftLintTestCase { func testIncludeAffectInits() { diff --git a/Tests/SwiftLintFrameworkTests/UnusedDeclarationConfigurationTests.swift b/Tests/BuiltInRulesTests/UnusedDeclarationConfigurationTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/UnusedDeclarationConfigurationTests.swift rename to Tests/BuiltInRulesTests/UnusedDeclarationConfigurationTests.swift diff --git a/Tests/SwiftLintFrameworkTests/UnusedOptionalBindingRuleTests.swift b/Tests/BuiltInRulesTests/UnusedOptionalBindingRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/UnusedOptionalBindingRuleTests.swift rename to Tests/BuiltInRulesTests/UnusedOptionalBindingRuleTests.swift index e210a3c337..801efc8842 100644 --- a/Tests/SwiftLintFrameworkTests/UnusedOptionalBindingRuleTests.swift +++ b/Tests/BuiltInRulesTests/UnusedOptionalBindingRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers final class UnusedOptionalBindingRuleTests: SwiftLintTestCase { func testDefaultConfiguration() { diff --git a/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift b/Tests/BuiltInRulesTests/VerticalWhitespaceRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift rename to Tests/BuiltInRulesTests/VerticalWhitespaceRuleTests.swift index deba0b0844..7f8589f401 100644 --- a/Tests/SwiftLintFrameworkTests/VerticalWhitespaceRuleTests.swift +++ b/Tests/BuiltInRulesTests/VerticalWhitespaceRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class VerticalWhitespaceRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift b/Tests/BuiltInRulesTests/XCTSpecificMatcherRuleTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift rename to Tests/BuiltInRulesTests/XCTSpecificMatcherRuleTests.swift index 90c78cfada..9b5a034f20 100644 --- a/Tests/SwiftLintFrameworkTests/XCTSpecificMatcherRuleTests.swift +++ b/Tests/BuiltInRulesTests/XCTSpecificMatcherRuleTests.swift @@ -1,4 +1,5 @@ @testable import SwiftLintBuiltInRules +import TestHelpers import XCTest final class XCTSpecificMatcherRuleTests: SwiftLintTestCase { @@ -182,7 +183,7 @@ final class XCTSpecificMatcherRuleTests: SwiftLintTestCase { private func violations(_ example: Example) -> [StyleViolation] { guard let config = makeConfig(nil, XCTSpecificMatcherRule.identifier) else { return [] } - return SwiftLintFrameworkTests.violations(example, config: config) + return TestHelpers.violations(example, config: config) } private func noViolation(in example: String) -> Bool { diff --git a/Tests/ExtraRulesTests/ExtraRulesTests.swift b/Tests/ExtraRulesTests/ExtraRulesTests.swift index c3b39c6871..ee63d3f8e6 100644 --- a/Tests/ExtraRulesTests/ExtraRulesTests.swift +++ b/Tests/ExtraRulesTests/ExtraRulesTests.swift @@ -1,5 +1,5 @@ @testable import SwiftLintExtraRules -import SwiftLintTestHelpers +import TestHelpers final class ExtraRulesTests: SwiftLintTestCase { func testWithDefaultConfiguration() { diff --git a/Tests/SwiftLintFrameworkTests/AccessControlLevelTests.swift b/Tests/FrameworkTests/AccessControlLevelTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/AccessControlLevelTests.swift rename to Tests/FrameworkTests/AccessControlLevelTests.swift diff --git a/Tests/SwiftLintFrameworkTests/BaselineTests.swift b/Tests/FrameworkTests/BaselineTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/BaselineTests.swift rename to Tests/FrameworkTests/BaselineTests.swift diff --git a/Tests/SwiftLintFrameworkTests/CodeIndentingRewriterTests.swift b/Tests/FrameworkTests/CodeIndentingRewriterTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/CodeIndentingRewriterTests.swift rename to Tests/FrameworkTests/CodeIndentingRewriterTests.swift diff --git a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift b/Tests/FrameworkTests/CollectingRuleTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift rename to Tests/FrameworkTests/CollectingRuleTests.swift index f3c7836772..3ff16a1dd6 100644 --- a/Tests/SwiftLintFrameworkTests/CollectingRuleTests.swift +++ b/Tests/FrameworkTests/CollectingRuleTests.swift @@ -1,5 +1,5 @@ @testable import SwiftLintFramework -import SwiftLintTestHelpers +import TestHelpers import XCTest final class CollectingRuleTests: SwiftLintTestCase { diff --git a/Tests/SwiftLintFrameworkTests/CommandTests.swift b/Tests/FrameworkTests/CommandTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/CommandTests.swift rename to Tests/FrameworkTests/CommandTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift b/Tests/FrameworkTests/ConfigurationAliasesTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ConfigurationAliasesTests.swift rename to Tests/FrameworkTests/ConfigurationAliasesTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift b/Tests/FrameworkTests/ConfigurationTests+Mock.swift similarity index 92% rename from Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift rename to Tests/FrameworkTests/ConfigurationTests+Mock.swift index 02513c1d91..212a5391cc 100644 --- a/Tests/SwiftLintFrameworkTests/ConfigurationTests+Mock.swift +++ b/Tests/FrameworkTests/ConfigurationTests+Mock.swift @@ -6,7 +6,7 @@ import SwiftLintFramework internal extension ConfigurationTests { enum Mock { // MARK: Test Resources Path - static let testResourcesPath: String = TestResources.path + static let testResourcesPath: String = TestResources.path() // MARK: Directory Paths enum Dir { @@ -82,16 +82,3 @@ internal extension ConfigurationTests { } } } - -struct RuleMock: Rule { - var configuration = SeverityConfiguration(.warning) - var configurationDescription: some Documentable { RuleConfigurationOption.noOptions } - - static let description = RuleDescription(identifier: "RuleMock", name: "", - description: "", kind: .style) - - init() { /* conformance for test */ } - init(configuration _: Any) throws { self.init() } - - func validate(file _: SwiftLintFile) -> [StyleViolation] { [] } -} diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift b/Tests/FrameworkTests/ConfigurationTests+MultipleConfigs.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ConfigurationTests+MultipleConfigs.swift rename to Tests/FrameworkTests/ConfigurationTests+MultipleConfigs.swift diff --git a/Tests/SwiftLintFrameworkTests/ConfigurationTests.swift b/Tests/FrameworkTests/ConfigurationTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ConfigurationTests.swift rename to Tests/FrameworkTests/ConfigurationTests.swift diff --git a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift b/Tests/FrameworkTests/CustomRulesTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/CustomRulesTests.swift rename to Tests/FrameworkTests/CustomRulesTests.swift index 56f3e1f9bd..87691f4e79 100644 --- a/Tests/SwiftLintFrameworkTests/CustomRulesTests.swift +++ b/Tests/FrameworkTests/CustomRulesTests.swift @@ -1,6 +1,7 @@ import SourceKittenFramework @testable import SwiftLintCore @testable import SwiftLintFramework +import TestHelpers import XCTest // swiftlint:disable file_length @@ -8,7 +9,7 @@ import XCTest final class CustomRulesTests: SwiftLintTestCase { private typealias Configuration = RegexConfiguration - private var testFile: SwiftLintFile { SwiftLintFile(path: "\(testResourcesPath)/test.txt")! } + private var testFile: SwiftLintFile { SwiftLintFile(path: "\(TestResources.path())/test.txt")! } func testCustomRuleConfigurationSetsCorrectlyWithMatchKinds() { let configDict = [ @@ -544,7 +545,7 @@ final class CustomRulesTests: SwiftLintTestCase { "custom_rules": customRules, ] let configuration = try SwiftLintFramework.Configuration(dict: configDict) - return SwiftLintTestHelpers.violations( + return TestHelpers.violations( example.skipWrappingInCommentTest(), config: configuration ) diff --git a/Tests/SwiftLintFrameworkTests/DisableAllTests.swift b/Tests/FrameworkTests/DisableAllTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/DisableAllTests.swift rename to Tests/FrameworkTests/DisableAllTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ExampleTests.swift b/Tests/FrameworkTests/ExampleTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ExampleTests.swift rename to Tests/FrameworkTests/ExampleTests.swift diff --git a/Tests/SwiftLintFrameworkTests/Exports.swift b/Tests/FrameworkTests/Exports.swift similarity index 75% rename from Tests/SwiftLintFrameworkTests/Exports.swift rename to Tests/FrameworkTests/Exports.swift index e9e69760b6..f1bf033a9e 100644 --- a/Tests/SwiftLintFrameworkTests/Exports.swift +++ b/Tests/FrameworkTests/Exports.swift @@ -1,3 +1,3 @@ // These imports are re-exported to make them available implicitly to all files. // swiftlint:disable:next unused_import -@_exported import SwiftLintTestHelpers +@_exported import TestHelpers diff --git a/Tests/SwiftLintFrameworkTests/ExtendedNSStringTests.swift b/Tests/FrameworkTests/ExtendedNSStringTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ExtendedNSStringTests.swift rename to Tests/FrameworkTests/ExtendedNSStringTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ExtendedStringTests.swift b/Tests/FrameworkTests/ExtendedStringTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ExtendedStringTests.swift rename to Tests/FrameworkTests/ExtendedStringTests.swift diff --git a/Tests/SwiftLintFrameworkTests/GlobTests.swift b/Tests/FrameworkTests/GlobTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/GlobTests.swift rename to Tests/FrameworkTests/GlobTests.swift index 81b53677b2..11b1e17730 100644 --- a/Tests/SwiftLintFrameworkTests/GlobTests.swift +++ b/Tests/FrameworkTests/GlobTests.swift @@ -1,9 +1,10 @@ @testable import SwiftLintFramework +import TestHelpers import XCTest final class GlobTests: SwiftLintTestCase { private var mockPath: String { - testResourcesPath.stringByAppendingPathComponent("ProjectMock") + TestResources.path().stringByAppendingPathComponent("ProjectMock") } func testNonExistingDirectory() { diff --git a/Tests/SwiftLintFrameworkTests/LineEndingTests.swift b/Tests/FrameworkTests/LineEndingTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/LineEndingTests.swift rename to Tests/FrameworkTests/LineEndingTests.swift diff --git a/Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift b/Tests/FrameworkTests/LintOrAnalyzeOptionsTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/LintOrAnalyzeOptionsTests.swift rename to Tests/FrameworkTests/LintOrAnalyzeOptionsTests.swift diff --git a/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift b/Tests/FrameworkTests/LinterCacheTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/LinterCacheTests.swift rename to Tests/FrameworkTests/LinterCacheTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift b/Tests/FrameworkTests/ModifierOrderTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ModifierOrderTests.swift rename to Tests/FrameworkTests/ModifierOrderTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ParserDiagnosticsTests.swift b/Tests/FrameworkTests/ParserDiagnosticsTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/ParserDiagnosticsTests.swift rename to Tests/FrameworkTests/ParserDiagnosticsTests.swift diff --git a/Tests/SwiftLintFrameworkTests/RegexConfigurationTests.swift b/Tests/FrameworkTests/RegexConfigurationTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/RegexConfigurationTests.swift rename to Tests/FrameworkTests/RegexConfigurationTests.swift diff --git a/Tests/SwiftLintFrameworkTests/RegionTests.swift b/Tests/FrameworkTests/RegionTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/RegionTests.swift rename to Tests/FrameworkTests/RegionTests.swift diff --git a/Tests/SwiftLintFrameworkTests/ReporterTests.swift b/Tests/FrameworkTests/ReporterTests.swift similarity index 97% rename from Tests/SwiftLintFrameworkTests/ReporterTests.swift rename to Tests/FrameworkTests/ReporterTests.swift index b6c30be294..369eeb1be6 100644 --- a/Tests/SwiftLintFrameworkTests/ReporterTests.swift +++ b/Tests/FrameworkTests/ReporterTests.swift @@ -2,6 +2,7 @@ import Foundation import SourceKittenFramework @testable import SwiftLintBuiltInRules @testable import SwiftLintFramework +import TestHelpers import XCTest final class ReporterTests: SwiftLintTestCase { @@ -41,7 +42,7 @@ final class ReporterTests: SwiftLintTestCase { } private func stringFromFile(_ filename: String) -> String { - SwiftLintFile(path: "\(testResourcesPath)/\(filename)")!.contents + SwiftLintFile(path: "\(TestResources.path())/\(filename)")!.contents } func testXcodeReporter() throws { @@ -208,7 +209,7 @@ final class ReporterTests: SwiftLintTestCase { let convertedReference = try stringConverter(reference) let convertedReporterOutput = try stringConverter(reporterOutput) if convertedReference != convertedReporterOutput { - let referenceURL = URL(fileURLWithPath: "\(testResourcesPath)/\(referenceFile)") + let referenceURL = URL(fileURLWithPath: "\(TestResources.path())/\(referenceFile)") try reporterOutput.replacingOccurrences( of: FileManager.default.currentDirectoryPath, with: "${CURRENT_WORKING_DIRECTORY}" diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedCSVReporterOutput.csv b/Tests/FrameworkTests/Resources/CannedCSVReporterOutput.csv similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedCSVReporterOutput.csv rename to Tests/FrameworkTests/Resources/CannedCSVReporterOutput.csv diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedCheckstyleReporterOutput.xml b/Tests/FrameworkTests/Resources/CannedCheckstyleReporterOutput.xml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedCheckstyleReporterOutput.xml rename to Tests/FrameworkTests/Resources/CannedCheckstyleReporterOutput.xml diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json b/Tests/FrameworkTests/Resources/CannedCodeClimateReporterOutput.json similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedCodeClimateReporterOutput.json rename to Tests/FrameworkTests/Resources/CannedCodeClimateReporterOutput.json diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedEmojiReporterOutput.txt b/Tests/FrameworkTests/Resources/CannedEmojiReporterOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedEmojiReporterOutput.txt rename to Tests/FrameworkTests/Resources/CannedEmojiReporterOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedGitHubActionsLoggingReporterOutput.txt b/Tests/FrameworkTests/Resources/CannedGitHubActionsLoggingReporterOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedGitHubActionsLoggingReporterOutput.txt rename to Tests/FrameworkTests/Resources/CannedGitHubActionsLoggingReporterOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedGitLabJUnitReporterOutput.xml b/Tests/FrameworkTests/Resources/CannedGitLabJUnitReporterOutput.xml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedGitLabJUnitReporterOutput.xml rename to Tests/FrameworkTests/Resources/CannedGitLabJUnitReporterOutput.xml diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedHTMLReporterOutput.html b/Tests/FrameworkTests/Resources/CannedHTMLReporterOutput.html similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedHTMLReporterOutput.html rename to Tests/FrameworkTests/Resources/CannedHTMLReporterOutput.html diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedJSONReporterOutput.json b/Tests/FrameworkTests/Resources/CannedJSONReporterOutput.json similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedJSONReporterOutput.json rename to Tests/FrameworkTests/Resources/CannedJSONReporterOutput.json diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedJunitReporterOutput.xml b/Tests/FrameworkTests/Resources/CannedJunitReporterOutput.xml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedJunitReporterOutput.xml rename to Tests/FrameworkTests/Resources/CannedJunitReporterOutput.xml diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedMarkdownReporterOutput.md b/Tests/FrameworkTests/Resources/CannedMarkdownReporterOutput.md similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedMarkdownReporterOutput.md rename to Tests/FrameworkTests/Resources/CannedMarkdownReporterOutput.md diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedRelativePathReporterOutput.txt b/Tests/FrameworkTests/Resources/CannedRelativePathReporterOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedRelativePathReporterOutput.txt rename to Tests/FrameworkTests/Resources/CannedRelativePathReporterOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedSARIFReporterOutput.json b/Tests/FrameworkTests/Resources/CannedSARIFReporterOutput.json similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedSARIFReporterOutput.json rename to Tests/FrameworkTests/Resources/CannedSARIFReporterOutput.json diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedSonarQubeReporterOutput.json b/Tests/FrameworkTests/Resources/CannedSonarQubeReporterOutput.json similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedSonarQubeReporterOutput.json rename to Tests/FrameworkTests/Resources/CannedSonarQubeReporterOutput.json diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedSummaryReporterNoViolationsOutput.txt b/Tests/FrameworkTests/Resources/CannedSummaryReporterNoViolationsOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedSummaryReporterNoViolationsOutput.txt rename to Tests/FrameworkTests/Resources/CannedSummaryReporterNoViolationsOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedSummaryReporterOutput.txt b/Tests/FrameworkTests/Resources/CannedSummaryReporterOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedSummaryReporterOutput.txt rename to Tests/FrameworkTests/Resources/CannedSummaryReporterOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/CannedXcodeReporterOutput.txt b/Tests/FrameworkTests/Resources/CannedXcodeReporterOutput.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/CannedXcodeReporterOutput.txt rename to Tests/FrameworkTests/Resources/CannedXcodeReporterOutput.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle1/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle1/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle1/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle1/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/child.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/child.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/child.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle2/child.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/Folder/child2.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/Folder/child2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/Folder/child2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/Folder/child2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/child1.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/child1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/child1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/Main/child1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/child3.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/child3.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/child3.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle3/child3.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/child.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/child.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/child.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/child.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Cycle4/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/Folder/child3.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/Folder/child3.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/Folder/child3.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/Folder/child3.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child1.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child2.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/child2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/Main/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/child4.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/child4.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test1/child4.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test1/child4.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child1.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child2.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/child2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ChildConfig/Test2/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ChildConfig/Test2/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Directory.swift/DirectoryLevel1.swift b/Tests/FrameworkTests/Resources/ProjectMock/Directory.swift/DirectoryLevel1.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Directory.swift/DirectoryLevel1.swift rename to Tests/FrameworkTests/Resources/ProjectMock/Directory.swift/DirectoryLevel1.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/EmptyFolder/.gitkeep b/Tests/FrameworkTests/Resources/ProjectMock/EmptyFolder/.gitkeep similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/EmptyFolder/.gitkeep rename to Tests/FrameworkTests/Resources/ProjectMock/EmptyFolder/.gitkeep diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level0.swift b/Tests/FrameworkTests/Resources/ProjectMock/Level0.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level0.swift rename to Tests/FrameworkTests/Resources/ProjectMock/Level0.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level1.swift b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level1.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level1.swift rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level1.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level2.swift b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level2.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level2.swift rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level2.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/Level3.swift b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/Level3.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/Level3.swift rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/Level3/Level3.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_disabled.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_disabled.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_disabled.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_disabled.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_only.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_only.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_only.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_only.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_reconfig.yml b/Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_reconfig.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_reconfig.yml rename to Tests/FrameworkTests/Resources/ProjectMock/Level1/Level2/custom_rules_reconfig.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Main.swift b/Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Main.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Main.swift rename to Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Main.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/Sub.swift b/Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/Sub.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/Sub.swift rename to Tests/FrameworkTests/Resources/ProjectMock/NestedConfig/Test/Sub/Sub.swift diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle1/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle1/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle1/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle1/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/parent.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/parent.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/parent.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle2/parent.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/parent.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/parent.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/parent.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Cycle3/parent.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent1.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent2.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test1/parent2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent1.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent2.yml b/Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/ParentConfig/Test2/parent2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/child.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/child.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/child.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/child.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Child/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Child/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Cycle/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Cycle/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Cycle/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Cycle/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/.swiftlint.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/.swiftlint.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/.swiftlint.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/.swiftlint.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child1.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child1.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child1.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child1.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child2.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child2.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child2.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/LocalRef/child2.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/expected.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/expected.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/expected.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/expected.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/main.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/main.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/main.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/main.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/parent.yml b/Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/parent.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/parent.yml rename to Tests/FrameworkTests/Resources/ProjectMock/RemoteConfig/Parent/parent.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom.yml b/Tests/FrameworkTests/Resources/ProjectMock/custom.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom.yml rename to Tests/FrameworkTests/Resources/ProjectMock/custom.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_included_excluded.yml b/Tests/FrameworkTests/Resources/ProjectMock/custom_included_excluded.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_included_excluded.yml rename to Tests/FrameworkTests/Resources/ProjectMock/custom_included_excluded.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_rules.yml b/Tests/FrameworkTests/Resources/ProjectMock/custom_rules.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_rules.yml rename to Tests/FrameworkTests/Resources/ProjectMock/custom_rules.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_rules_only.yml b/Tests/FrameworkTests/Resources/ProjectMock/custom_rules_only.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/ProjectMock/custom_rules_only.yml rename to Tests/FrameworkTests/Resources/ProjectMock/custom_rules_only.yml diff --git a/Tests/SwiftLintFrameworkTests/Resources/test.txt b/Tests/FrameworkTests/Resources/test.txt similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/test.txt rename to Tests/FrameworkTests/Resources/test.txt diff --git a/Tests/SwiftLintFrameworkTests/Resources/test.yml b/Tests/FrameworkTests/Resources/test.yml similarity index 100% rename from Tests/SwiftLintFrameworkTests/Resources/test.yml rename to Tests/FrameworkTests/Resources/test.yml diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift similarity index 99% rename from Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift rename to Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift index c3a00722fd..8e15565c37 100644 --- a/Tests/SwiftLintFrameworkTests/RuleConfigurationDescriptionTests.swift +++ b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift @@ -1,5 +1,5 @@ @testable import SwiftLintCore -import SwiftLintTestHelpers +import TestHelpers import XCTest // swiftlint:disable file_length diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift b/Tests/FrameworkTests/RuleConfigurationTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift rename to Tests/FrameworkTests/RuleConfigurationTests.swift diff --git a/Tests/SwiftLintFrameworkTests/RuleTests.swift b/Tests/FrameworkTests/RuleTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/RuleTests.swift rename to Tests/FrameworkTests/RuleTests.swift diff --git a/Tests/SwiftLintFrameworkTests/RulesTests.swift b/Tests/FrameworkTests/RulesTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/RulesTests.swift rename to Tests/FrameworkTests/RulesTests.swift diff --git a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift b/Tests/FrameworkTests/SourceKitCrashTests.swift similarity index 98% rename from Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift rename to Tests/FrameworkTests/SourceKitCrashTests.swift index 320ef5c47b..fbb429c92b 100644 --- a/Tests/SwiftLintFrameworkTests/SourceKitCrashTests.swift +++ b/Tests/FrameworkTests/SourceKitCrashTests.swift @@ -47,7 +47,7 @@ final class SourceKitCrashTests: SwiftLintTestCase { } func testRulesWithFileThatCrashedSourceKitService() throws { - let file = try XCTUnwrap(SwiftLintFile(path: "\(TestResources.path)/ProjectMock/Level0.swift")) + let file = try XCTUnwrap(SwiftLintFile(path: "\(TestResources.path())/ProjectMock/Level0.swift")) file.sourcekitdFailed = true file.assertHandler = { XCTFail("If this called, rule's SourceKitFreeRule is not properly configured") diff --git a/Tests/SwiftLintFrameworkTests/StringExtensionTests.swift b/Tests/FrameworkTests/StringExtensionTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/StringExtensionTests.swift rename to Tests/FrameworkTests/StringExtensionTests.swift diff --git a/Tests/SwiftLintFrameworkTests/StringViewExtensionTests.swift b/Tests/FrameworkTests/StringViewExtensionTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/StringViewExtensionTests.swift rename to Tests/FrameworkTests/StringViewExtensionTests.swift diff --git a/Tests/SwiftLintFrameworkTests/SwiftLintFileTests.swift b/Tests/FrameworkTests/SwiftLintFileTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/SwiftLintFileTests.swift rename to Tests/FrameworkTests/SwiftLintFileTests.swift diff --git a/Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift b/Tests/FrameworkTests/SwiftVersionTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/SwiftVersionTests.swift rename to Tests/FrameworkTests/SwiftVersionTests.swift diff --git a/Tests/SwiftLintFrameworkTests/YamlParserTests.swift b/Tests/FrameworkTests/YamlParserTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/YamlParserTests.swift rename to Tests/FrameworkTests/YamlParserTests.swift diff --git a/Tests/SwiftLintFrameworkTests/YamlSwiftLintTests.swift b/Tests/FrameworkTests/YamlSwiftLintTests.swift similarity index 96% rename from Tests/SwiftLintFrameworkTests/YamlSwiftLintTests.swift rename to Tests/FrameworkTests/YamlSwiftLintTests.swift index a001de08d4..23d9bfb2f8 100644 --- a/Tests/SwiftLintFrameworkTests/YamlSwiftLintTests.swift +++ b/Tests/FrameworkTests/YamlSwiftLintTests.swift @@ -39,6 +39,6 @@ final class YamlSwiftLintTests: SwiftLintTestCase { } private func getTestYaml() throws -> String { - try String(contentsOfFile: "\(testResourcesPath)/test.yml", encoding: .utf8) + try String(contentsOfFile: "\(TestResources.path())/test.yml", encoding: .utf8) } } diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 6ef660cb4d..e5b5625317 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -2,7 +2,7 @@ // DO NOT EDIT @testable import SwiftLintBuiltInRules @testable import SwiftLintCore -import SwiftLintTestHelpers +import TestHelpers // swiftlint:disable:next blanket_disable_command // swiftlint:disable file_length single_test_class type_name diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index 54e12ec8fc..f195393ea9 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -1,6 +1,6 @@ import Foundation import SwiftLintFramework -import SwiftLintTestHelpers +import TestHelpers import XCTest private let config: Configuration = { diff --git a/Tests/SwiftLintFrameworkTests/XCTestCase+BundlePath.swift b/Tests/SwiftLintFrameworkTests/XCTestCase+BundlePath.swift deleted file mode 100644 index f20eb5b175..0000000000 --- a/Tests/SwiftLintFrameworkTests/XCTestCase+BundlePath.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation -import XCTest - -enum TestResources { - static var path: String { - if let rootProjectDirectory = ProcessInfo.processInfo.environment["BUILD_WORKSPACE_DIRECTORY"] { - return "\(rootProjectDirectory)/Tests/SwiftLintFrameworkTests/Resources" - } - - return URL(fileURLWithPath: #filePath, isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("Resources") - .path - .absolutePathStandardized() - } -} - -extension XCTestCase { - var testResourcesPath: String { TestResources.path } -} diff --git a/Tests/SwiftLintTestHelpers/RuleDescription+Examples.swift b/Tests/TestHelpers/RuleDescription+Examples.swift similarity index 100% rename from Tests/SwiftLintTestHelpers/RuleDescription+Examples.swift rename to Tests/TestHelpers/RuleDescription+Examples.swift diff --git a/Tests/TestHelpers/RuleMock.swift b/Tests/TestHelpers/RuleMock.swift new file mode 100644 index 0000000000..b1b15d131a --- /dev/null +++ b/Tests/TestHelpers/RuleMock.swift @@ -0,0 +1,19 @@ +import SwiftLintCore + +public struct RuleMock: Rule { + var configurationDescription: some Documentable { RuleConfigurationOption.noOptions } + + public var configuration = SeverityConfiguration(.warning) + + public static let description = RuleDescription( + identifier: "RuleMock", + name: "", + description: "", + kind: .style + ) + + public init() { /* conformance for test */ } + public init(configuration _: Any) throws { self.init() } + + public func validate(file _: SwiftLintFile) -> [StyleViolation] { [] } +} diff --git a/Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift b/Tests/TestHelpers/SwiftLintTestCase.swift similarity index 100% rename from Tests/SwiftLintTestHelpers/SwiftLintTestCase.swift rename to Tests/TestHelpers/SwiftLintTestCase.swift diff --git a/Tests/SwiftLintTestHelpers/TestHelpers.swift b/Tests/TestHelpers/TestHelpers.swift similarity index 100% rename from Tests/SwiftLintTestHelpers/TestHelpers.swift rename to Tests/TestHelpers/TestHelpers.swift diff --git a/Tests/TestHelpers/TestResources.swift b/Tests/TestHelpers/TestResources.swift new file mode 100644 index 0000000000..69de7e6a56 --- /dev/null +++ b/Tests/TestHelpers/TestResources.swift @@ -0,0 +1,14 @@ +import Foundation + +public enum TestResources { + public static func path(_ calleePath: String = #filePath) -> String { + let folder = URL(fileURLWithPath: calleePath, isDirectory: false).deletingLastPathComponent() + if let rootProjectDirectory = ProcessInfo.processInfo.environment["BUILD_WORKSPACE_DIRECTORY"] { + return "\(rootProjectDirectory)/Tests/\(folder.lastPathComponent)/Resources" + } + return folder + .appendingPathComponent("Resources") + .path + .absolutePathStandardized() + } +} From ec33a022786ff6879aa78ae52fbab1f3adce7c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Dec 2024 13:08:42 +0100 Subject: [PATCH 138/260] Ensure plugins still build with older Swift versions (#5926) --- azure-pipelines.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3ec9125914..3952eb9b33 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,6 +30,29 @@ jobs: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test +- job: Plugins # Plugins shall be able to run on older Swift versions. + strategy: + maxParallel: 10 + matrix: + ': macOS 13, Swift 5.9': + image: 'macOS-13' + xcode: '15.2' + ': macOS 14, Swift 5.10': + image: 'macOS-14' + xcode: '15.4' + ': macOS 14, Swift 6': + image: 'macOS-14' + xcode: '16.1' + pool: + vmImage: $(image) + variables: + DEVELOPER_DIR: /Applications/Xcode_$(xcode).app + steps: + - script: swift build -c release --product SwiftLintCommandPlugin + displayName: Command Plugin + - script: swift build -c release --product SwiftLintBuildToolPlugin + displayName: Build Tool Plugin + - job: CocoaPods pool: vmImage: 'macOS-14' From 592b8eb3ce442be605355af126e3892ba9bf0ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Dec 2024 13:10:38 +0100 Subject: [PATCH 139/260] Run tests for more OS, Swift, Xcode combinations (#5925) --- azure-pipelines.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3952eb9b33..7f66d03616 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,9 +2,9 @@ trigger: - main jobs: -- job: Linux +- job: Ubuntu pool: - vmImage: 'ubuntu-24.04' + vmImage: 'ubuntu-24.04' # "Noble Numbat" strategy: maxParallel: 10 matrix: @@ -19,9 +19,18 @@ jobs: strategy: maxParallel: 10 matrix: - '14': + '14, Xcode 15.4': image: 'macOS-14' xcode: '15.4' + '14, Xcode 16.1': + image: 'macOS-14' + xcode: '16.1' + '15, Xcode 15.4': + image: 'macOS-15' + xcode: '15.4' + '15, Xcode 16.2': + image: 'macOS-15' + xcode: '16.2' pool: vmImage: $(image) variables: @@ -57,7 +66,7 @@ jobs: pool: vmImage: 'macOS-14' variables: - DEVELOPER_DIR: /Applications/Xcode_16.app + DEVELOPER_DIR: /Applications/Xcode_16.1.app steps: - script: bundle install --path vendor/bundle displayName: bundle install From 6a1569faa2d786dca7c4c398c6e1c436750a5836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Dec 2024 13:16:37 +0100 Subject: [PATCH 140/260] Sort targets (#5927) --- Package.swift | 98 +++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/Package.swift b/Package.swift index d4ac4a774f..04dbb1f31a 100644 --- a/Package.swift +++ b/Package.swift @@ -39,6 +39,26 @@ let package = Package( .package(url: "https://github.com/ileitch/swift-filename-matcher", .upToNextMinor(from: "2.0.0")), ], targets: [ + .executableTarget( + name: "swiftlint", + dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), + "CollectionConcurrencyKit", + "SwiftLintFramework", + "SwiftyTextTable", + ], + swiftSettings: swiftFeatures + strictConcurrency + ), + .target( + name: "SwiftLintFramework", + dependencies: [ + "SwiftLintBuiltInRules", + "SwiftLintCore", + "SwiftLintExtraRules", + "CollectionConcurrencyKit", + ], + swiftSettings: swiftFeatures + ), .plugin( name: "SwiftLintBuildToolPlugin", capability: .buildTool(), @@ -56,23 +76,6 @@ let package = Package( ), dependencies: swiftLintPluginDependencies ), - .executableTarget( - name: "swiftlint", - dependencies: [ - .product(name: "ArgumentParser", package: "swift-argument-parser"), - "CollectionConcurrencyKit", - "SwiftLintFramework", - "SwiftyTextTable", - ], - swiftSettings: swiftFeatures + strictConcurrency - ), - .testTarget( - name: "CLITests", - dependencies: [ - "SwiftLintFramework", - ], - swiftSettings: swiftFeatures - ), .target( name: "SwiftLintCore", dependencies: [ @@ -101,31 +104,22 @@ let package = Package( dependencies: ["SwiftLintCore"], swiftSettings: swiftFeatures + strictConcurrency ), - .target( - name: "SwiftLintFramework", - dependencies: [ - "SwiftLintBuiltInRules", - "SwiftLintCore", - "SwiftLintExtraRules", - "CollectionConcurrencyKit", - ], - swiftSettings: swiftFeatures - ), .target(name: "DyldWarningWorkaround"), - .target( - name: "TestHelpers", + .macro( + name: "SwiftLintCoreMacros", dependencies: [ - "SwiftLintFramework" + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ], - path: "Tests/TestHelpers", - swiftSettings: swiftFeatures + path: "Source/SwiftLintCoreMacros", + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( - name: "FrameworkTests", + name: "BuiltInRulesTests", dependencies: [ + "SwiftLintBuiltInRules", "SwiftLintFramework", "TestHelpers", - "SwiftLintCoreMacros", ], exclude: [ "Resources", @@ -133,30 +127,26 @@ let package = Package( swiftSettings: swiftFeatures ), .testTarget( - name: "GeneratedTests", + name: "CLITests", dependencies: [ "SwiftLintFramework", - "TestHelpers", ], swiftSettings: swiftFeatures ), .testTarget( - name: "IntegrationTests", + name: "ExtraRulesTests", dependencies: [ "SwiftLintFramework", "TestHelpers", ], - exclude: [ - "default_rule_configurations.yml" - ], swiftSettings: swiftFeatures ), .testTarget( - name: "BuiltInRulesTests", + name: "FrameworkTests", dependencies: [ - "SwiftLintBuiltInRules", "SwiftLintFramework", "TestHelpers", + "SwiftLintCoreMacros", ], exclude: [ "Resources", @@ -164,21 +154,23 @@ let package = Package( swiftSettings: swiftFeatures ), .testTarget( - name: "ExtraRulesTests", + name: "GeneratedTests", dependencies: [ "SwiftLintFramework", "TestHelpers", ], swiftSettings: swiftFeatures ), - .macro( - name: "SwiftLintCoreMacros", + .testTarget( + name: "IntegrationTests", dependencies: [ - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + "SwiftLintFramework", + "TestHelpers", ], - path: "Source/SwiftLintCoreMacros", - swiftSettings: swiftFeatures + strictConcurrency + exclude: [ + "default_rule_configurations.yml" + ], + swiftSettings: swiftFeatures ), .testTarget( name: "MacroTests", @@ -188,6 +180,14 @@ let package = Package( ], swiftSettings: swiftFeatures ), + .target( + name: "TestHelpers", + dependencies: [ + "SwiftLintFramework" + ], + path: "Tests/TestHelpers", + swiftSettings: swiftFeatures + ), ] ) From e53d06b4e5fb79e5f9aa7bd4f71283b5b75c97c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 30 Dec 2024 15:09:10 +0100 Subject: [PATCH 141/260] Remove explicit framework dependency (#5928) --- Package.swift | 1 - .../ExpiringTodoRuleTests.swift | 93 +++++++------------ 2 files changed, 34 insertions(+), 60 deletions(-) diff --git a/Package.swift b/Package.swift index 04dbb1f31a..8e5ca12135 100644 --- a/Package.swift +++ b/Package.swift @@ -118,7 +118,6 @@ let package = Package( name: "BuiltInRulesTests", dependencies: [ "SwiftLintBuiltInRules", - "SwiftLintFramework", "TestHelpers", ], exclude: [ diff --git a/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift b/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift index b0dc3015f1..bfbe34d150 100644 --- a/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift +++ b/Tests/BuiltInRulesTests/ExpiringTodoRuleTests.swift @@ -1,11 +1,8 @@ @testable import SwiftLintBuiltInRules -import SwiftLintFramework import TestHelpers import XCTest final class ExpiringTodoRuleTests: SwiftLintTestCase { - private lazy var config: Configuration = makeConfiguration() - func testExpiringTodo() { verifyRule(ExpiringTodoRule.description, commentDoesntViolate: false) } @@ -37,38 +34,34 @@ final class ExpiringTodoRuleTests: SwiftLintTestCase { } func testExpiredCustomDelimiters() { - let ruleConfig: ExpiringTodoConfiguration = .init( + let ruleConfig = ExpiringTodoConfiguration( dateDelimiters: .init(opening: "<", closing: ">") ) - config = makeConfiguration(with: ruleConfig) - let example = Example("fatalError() // TODO: <\(dateString(for: .expired))> Implement") - let violations = self.violations(example) + let violations = self.violations(example, ruleConfig) XCTAssertEqual(violations.count, 1) XCTAssertEqual(violations.first!.reason, "TODO/FIXME has expired and must be resolved") } func testExpiredCustomSeparator() { - let ruleConfig: ExpiringTodoConfiguration = .init( + let ruleConfig = ExpiringTodoConfiguration( dateFormat: "MM-dd-yyyy", dateSeparator: "-" ) - config = makeConfiguration(with: ruleConfig) - - let example = Example("fatalError() // TODO: [\(dateString(for: .expired))] Implement") - let violations = self.violations(example) + let example = Example( + "fatalError() // TODO: [\(dateString(for: .expired, format: ruleConfig.dateFormat))] Implement" + ) + let violations = self.violations(example, ruleConfig) XCTAssertEqual(violations.count, 1) XCTAssertEqual(violations.first!.reason, "TODO/FIXME has expired and must be resolved") } func testExpiredCustomFormat() { - let ruleConfig: ExpiringTodoConfiguration = .init( - dateFormat: "yyyy/MM/dd" + let ruleConfig = ExpiringTodoConfiguration(dateFormat: "yyyy/MM/dd") + let example = Example( + "fatalError() // TODO: [\(dateString(for: .expired, format: ruleConfig.dateFormat))] Implement" ) - config = makeConfiguration(with: ruleConfig) - - let example = Example("fatalError() // TODO: [\(dateString(for: .expired))] Implement") - let violations = self.violations(example) + let violations = self.violations(example, ruleConfig) XCTAssertEqual(violations.count, 1) XCTAssertEqual(violations.first!.reason, "TODO/FIXME has expired and must be resolved") } @@ -131,30 +124,40 @@ final class ExpiringTodoRuleTests: SwiftLintTestCase { } func testBadExpiryTodoFormat() throws { - let ruleConfig: ExpiringTodoConfiguration = .init( + let ruleConfig = ExpiringTodoConfiguration( dateFormat: "dd/yyyy/MM" ) - config = makeConfiguration(with: ruleConfig) - let example = Example("fatalError() // TODO: [31/01/2020] Implement") - let violations = self.violations(example) + let violations = self.violations(example, ruleConfig) XCTAssertEqual(violations.count, 1) XCTAssertEqual(violations.first?.reason, "Expiring TODO/FIXME is incorrectly formatted") } - private func violations(_ example: Example) -> [StyleViolation] { - TestHelpers.violations(example, config: config) - } - - private func dateString(for status: ExpiringTodoRule.ExpiryViolationLevel) -> String { - let formatter: DateFormatter = .init() - formatter.dateFormat = config.ruleConfiguration.dateFormat - + private func violations(_ example: Example, _ config: ExpiringTodoConfiguration? = nil) -> [StyleViolation] { + let config = config ?? ExpiringTodoConfiguration() + let serializedConfig = [ + "expired_severity": config.expiredSeverity.severity.rawValue, + "approaching_expiry_severity": config.approachingExpirySeverity.severity.rawValue, + "bad_formatting_severity": config.badFormattingSeverity.severity.rawValue, + "approaching_expiry_threshold": config.approachingExpiryThreshold, + "date_format": config.dateFormat, + "date_delimiters": [ + "opening": config.dateDelimiters.opening, + "closing": config.dateDelimiters.closing, + ], + "date_separator": config.dateSeparator, + ] as [String: Any] + return TestHelpers.violations(example, config: makeConfig(serializedConfig, ExpiringTodoRule.identifier)!) + } + + private func dateString(for status: ExpiringTodoRule.ExpiryViolationLevel, format: String? = nil) -> String { + let formatter = DateFormatter() + formatter.dateFormat = format ?? ExpiringTodoConfiguration().dateFormat return formatter.string(from: date(for: status)) } private func date(for status: ExpiringTodoRule.ExpiryViolationLevel) -> Date { - let ruleConfiguration = config.ruleConfiguration + let ruleConfiguration = ExpiringTodoRule().configuration let daysToAdvance: Int @@ -174,32 +177,4 @@ final class ExpiringTodoRuleTests: SwiftLintTestCase { to: .init() )! } - - private func makeConfiguration(with ruleConfiguration: ExpiringTodoConfiguration? = nil) -> Configuration { - var serializedConfig: [String: Any]? - - if let config = ruleConfiguration { - serializedConfig = [ - "expired_severity": config.expiredSeverity.severity.rawValue, - "approaching_expiry_severity": config.approachingExpirySeverity.severity.rawValue, - "bad_formatting_severity": config.badFormattingSeverity.severity.rawValue, - "approaching_expiry_threshold": config.approachingExpiryThreshold, - "date_format": config.dateFormat, - "date_delimiters": [ - "opening": config.dateDelimiters.opening, - "closing": config.dateDelimiters.closing, - ], - "date_separator": config.dateSeparator, - ] - } - - return makeConfig(serializedConfig, ExpiringTodoRule.identifier)! - } -} - -fileprivate extension Configuration { - var ruleConfiguration: ExpiringTodoConfiguration { - // swiftlint:disable:next force_cast - (rules.first(where: { $0 is ExpiringTodoRule }) as! ExpiringTodoRule).configuration - } } From be25c1fc5e302381e9a787524813ca9b51541cbb Mon Sep 17 00:00:00 2001 From: "Jason N. White" Date: Wed, 1 Jan 2025 11:01:23 -0500 Subject: [PATCH 142/260] Update LICENSE, fix license year (#5930) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0420376276..dae3569e96 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020 Realm Inc. +Copyright (c) 2025 Realm Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From ae8aeb39953cc7ebe72003642ab38ad7586e3836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 3 Jan 2025 19:30:35 +0100 Subject: [PATCH 143/260] Add option to disable `redundant_discardable_let` in SwiftUI view bodies (#5929) --- CHANGELOG.md | 4 + ...RedundantDiscardableLetConfiguration.swift | 11 +++ .../Style/RedundantDiscardableLetRule.swift | 88 ++++++++++++++----- .../default_rule_configurations.yml | 1 + 4 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantDiscardableLetConfiguration.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b575048de4..6538b218b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,10 @@ [dk-talks](https://github.com/dk-talks) [SimplyDanny](https://github.com/SimplyDanny) +* Add option to disable `redundant_discardable_let` rule in SwiftUI view bodies. + [SimplyDanny](https://github.com/SimplyDanny) + [#3855](https://github.com/realm/SwiftLint/issues/3855) + * Add new `redundant_sendable` rule that triggers on `Sendable` conformances of types that are implicitly already `Sendable` due to being actor-isolated. It is enabled by default. diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantDiscardableLetConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantDiscardableLetConfiguration.swift new file mode 100644 index 0000000000..70a588991a --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/RedundantDiscardableLetConfiguration.swift @@ -0,0 +1,11 @@ +import SwiftLintCore + +@AutoConfigParser +struct RedundantDiscardableLetConfiguration: SeverityBasedRuleConfiguration { + typealias Parent = RedundantDiscardableLetRule + + @ConfigurationElement(key: "severity") + private(set) var severityConfiguration = SeverityConfiguration(.warning) + @ConfigurationElement(key: "ignore_swiftui_view_bodies") + private(set) var ignoreSwiftUIViewBodies = false +} diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/RedundantDiscardableLetRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/RedundantDiscardableLetRule.swift index 76904825a0..d7d6f2a1f6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/RedundantDiscardableLetRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/RedundantDiscardableLetRule.swift @@ -1,8 +1,8 @@ import SwiftSyntax -@SwiftSyntaxRule(explicitRewriter: true) +@SwiftSyntaxRule(correctable: true) struct RedundantDiscardableLetRule: Rule { - var configuration = SeverityConfiguration(.warning) + var configuration = RedundantDiscardableLetConfiguration() static let description = RuleDescription( identifier: "redundant_discardable_let", @@ -16,10 +16,22 @@ struct RedundantDiscardableLetRule: Rule { Example("let _: ExplicitType = foo()"), Example("while let _ = SplashStyle(rawValue: maxValue) { maxValue += 1 }"), Example("async let _ = await foo()"), + Example(""" + var body: some View { + let _ = foo() + return Text("Hello, World!") + } + """, configuration: ["ignore_swiftui_view_bodies": true]), ], triggeringExamples: [ Example("↓let _ = foo()"), Example("if _ = foo() { ↓let _ = bar() }"), + Example(""" + var body: some View { + ↓let _ = foo() + Text("Hello, World!") + } + """), ], corrections: [ Example("↓let _ = foo()"): Example("_ = foo()"), @@ -29,35 +41,63 @@ struct RedundantDiscardableLetRule: Rule { } private extension RedundantDiscardableLetRule { + private enum CodeBlockKind { + case normal + case view + } + final class Visitor: ViolationsSyntaxVisitor { - override func visitPost(_ node: VariableDeclSyntax) { - if node.hasRedundantDiscardableLetViolation { - violations.append(node.positionAfterSkippingLeadingTrivia) - } + private var codeBlockScopes = Stack() + + override func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind { + codeBlockScopes.push(node.isViewBody ? .view : .normal) + return .visitChildren } - } - final class Rewriter: ViolationsSyntaxRewriter { - override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - guard node.hasRedundantDiscardableLetViolation else { - return super.visit(node) - } + override func visitPost(_: AccessorBlockSyntax) { + codeBlockScopes.pop() + } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - let newNode = node - .with(\.bindingSpecifier, .keyword(.let, presence: .missing)) - .with(\.bindings, node.bindings.with(\.leadingTrivia, node.bindingSpecifier.leadingTrivia)) - return super.visit(newNode) + override func visit(_: CodeBlockSyntax) -> SyntaxVisitorContinueKind { + codeBlockScopes.push(.normal) + return .visitChildren + } + + override func visitPost(_: CodeBlockSyntax) { + codeBlockScopes.pop() + } + + override func visitPost(_ node: VariableDeclSyntax) { + if codeBlockScopes.peek() != .view || !configuration.ignoreSwiftUIViewBodies, + node.bindingSpecifier.tokenKind == .keyword(.let), + let binding = node.bindings.onlyElement, + binding.pattern.is(WildcardPatternSyntax.self), + binding.typeAnnotation == nil, + !node.modifiers.contains(where: { $0.name.text == "async" }) { + violations.append( + ReasonedRuleViolation( + position: node.bindingSpecifier.positionAfterSkippingLeadingTrivia, + correction: .init( + start: node.bindingSpecifier.positionAfterSkippingLeadingTrivia, + end: binding.pattern.positionAfterSkippingLeadingTrivia, + replacement: "" + ) + ) + ) + } } } } -private extension VariableDeclSyntax { - var hasRedundantDiscardableLetViolation: Bool { - bindingSpecifier.tokenKind == .keyword(.let) - && bindings.count == 1 - && bindings.first!.pattern.is(WildcardPatternSyntax.self) - && bindings.first!.typeAnnotation == nil - && modifiers.contains(where: { $0.name.text == "async" }) != true +private extension AccessorBlockSyntax { + var isViewBody: Bool { + if let binding = parent?.as(PatternBindingSyntax.self), + binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text == "body", + let type = binding.typeAnnotation?.type.as(SomeOrAnyTypeSyntax.self) { + return type.someOrAnySpecifier.text == "some" + && type.constraint.as(IdentifierTypeSyntax.self)?.name.text == "View" + && binding.parent?.parent?.is(VariableDeclSyntax.self) == true + } + return false } } diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 717d6aec93..76cefae7c2 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -795,6 +795,7 @@ reduce_into: opt-in: true redundant_discardable_let: severity: warning + ignore_swiftui_view_bodies: false meta: opt-in: false redundant_nil_coalescing: From 4e5911b9a688f1dcb9a1d7ca510aaa74fafb63a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 3 Jan 2025 19:37:52 +0100 Subject: [PATCH 144/260] Change wording (#5933) --- tools/oss-check | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/oss-check b/tools/oss-check index f1d0a05ca8..3e963c53d9 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -78,7 +78,7 @@ class Repo percent_change *= -1 end "Linting #{self} with this PR took #{@branch_duration}s " \ - "vs #{@main_duration}s on main (#{percent_change.to_i}\% #{faster_slower})" + "vs #{@main_duration}s on main (#{percent_change.to_i}\% #{faster_slower})." end end @@ -234,8 +234,8 @@ def diff_and_report_changes_to_danger @repos.each do |repo| if repo.main_exit_value != repo.branch_exit_value - warn "This PR changed the exit value when running on #{repo.name}: " \ - "(#{repo.main_exit_value} to #{repo.branch_exit_value})" + warn "This PR changed the exit value from #{repo.main_exit_value} to #{repo.branch_exit_value} when " \ + "running on #{repo.name}." # If the exit value changed, don't show the fixes or regressions for this # repo because it's likely due to a crash, and all violations would be noisy next @@ -334,7 +334,7 @@ unless @options[:force] full_version_main = `#{@working_dir}/builds/swiftlint-main version --verbose` if full_version_branch == full_version_main - message "Skipping OSSCheck because SwiftLint hasn't changed compared to 'main'" + message "Skipping OSSCheck because SwiftLint hasn't changed compared to 'main'." # Clean up clean_up unless @options[:skip_clean] exit From 28ce97c729c3188bb93e801c6c838470ae42ca43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 4 Jan 2025 00:01:12 +0100 Subject: [PATCH 145/260] Compare and report binary size changes in PRs (#5934) --- tools/oss-check | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/oss-check b/tools/oss-check index 3e963c53d9..d84753f7e2 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -287,6 +287,25 @@ def print_rules_changed end end +def report_binary_size + size_branch = File.size("#{@working_dir}/builds/swiftlint-branch") + size_main = File.size("#{@working_dir}/builds/swiftlint-main") + if size_branch == size_main + message "Building this branch resulted in the same binary size as when built on `main`." + else + percent_change = 100 * (size_branch - size_main) / size_main + faster_slower = size_branch < size_main ? 'smaller' : 'larger' + in_kilo_bytes = ->(size) { (size / 1024.0).round(2) } + msg = "Building this branch resulted in a binary size of #{in_kilo_bytes.call(size_branch)} KiB " \ + "vs #{in_kilo_bytes.call(size_main)} KiB when built on `main` (#{percent_change.to_i}\% #{faster_slower})." + if percent_change.abs < 2 + message msg + else + warn msg + end + end +end + ################################ # Script ################################ @@ -329,6 +348,9 @@ make_directory_structure build(branch) end +# Compare binary size of both builds. +report_binary_size + unless @options[:force] full_version_branch = `#{@working_dir}/builds/swiftlint-branch version --verbose` full_version_main = `#{@working_dir}/builds/swiftlint-main version --verbose` From 4f55943678d2efff734db3f7030b0afd9f1dde26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 4 Jan 2025 16:08:27 +0100 Subject: [PATCH 146/260] Lint PRs and show inline comments for violations (#5935) The long-running integration tests are no longer needed (in CI). Having violations reported right in the PR is much more convenient. --- .github/workflows/lint.yml | 15 +++++++++++++++ Tests/IntegrationTests/IntegrationTests.swift | 12 ++++++++++-- azure-pipelines.yml | 3 +++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..384ce840c2 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,15 @@ +name: Lint + +on: + pull_request: + branches: + - '*' + +jobs: + lint: + runs-on: ubuntu-24.04 # "Noble Numbat" + container: swift:6.0-noble + steps: + - uses: actions/checkout@v4 + - name: Lint + run: swift run swiftlint --reporter github-actions-logging --strict 2> /dev/null diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index f195393ea9..877686a120 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -14,7 +14,11 @@ private let config: Configuration = { }() final class IntegrationTests: SwiftLintTestCase { - func testSwiftLintLints() { + func testSwiftLintLints() throws { + try XCTSkipUnless( + ProcessInfo.processInfo.environment["CI"] == nil, + "Will be covered by separate linting job" + ) // This is as close as we're ever going to get to a self-hosting linter. let swiftFiles = config.lintableFiles( inPath: "", @@ -36,7 +40,11 @@ final class IntegrationTests: SwiftLintTestCase { } } - func testSwiftLintAutoCorrects() { + func testSwiftLintAutoCorrects() throws { + try XCTSkipUnless( + ProcessInfo.processInfo.environment["CI"] == nil, + "Corrections are not verified in CI" + ) let swiftFiles = config.lintableFiles( inPath: "", forceExclude: false, diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7f66d03616..323dff9baa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,9 @@ trigger: - main +variables: + CI: 'true' + jobs: - job: Ubuntu pool: From 7321f2bbd06f2df1605314ae9336dc09931b285b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 4 Jan 2025 17:29:26 +0100 Subject: [PATCH 147/260] Warn about implicit overrides (#5936) --- BUILD | 1 + Source/SwiftLintFramework/Reporters/Reporter.swift | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/BUILD b/BUILD index 59db0a9498..10f3d6fc52 100644 --- a/BUILD +++ b/BUILD @@ -32,6 +32,7 @@ copts = [ "ForwardTrailingClosures", "-enable-upcoming-feature", "ImplicitOpenExistentials", + "-Xfrontend", "-warn-implicit-overrides", ] strict_concurrency_copts = [ diff --git a/Source/SwiftLintFramework/Reporters/Reporter.swift b/Source/SwiftLintFramework/Reporters/Reporter.swift index 18667c9d65..f0488e4af6 100644 --- a/Source/SwiftLintFramework/Reporters/Reporter.swift +++ b/Source/SwiftLintFramework/Reporters/Reporter.swift @@ -10,9 +10,6 @@ public protocol Reporter: CustomStringConvertible { /// A more detailed description of the reporter's output. static var description: String { get } - /// For CustomStringConvertible conformance. - var description: String { get } - /// Return a string with the report for the specified violations. /// /// - parameter violations: The violations to report. From 83f9e3b45b54714139ab0a660e75964b3ffc9948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 4 Jan 2025 21:19:05 +0100 Subject: [PATCH 148/260] Build framework with targeted strict concurrency checks (#5937) --- BUILD | 6 +++++- Package.swift | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/BUILD b/BUILD index 10f3d6fc52..53907b3b7b 100644 --- a/BUILD +++ b/BUILD @@ -39,6 +39,10 @@ strict_concurrency_copts = [ "-Xfrontend", "-strict-concurrency=complete", ] +targeted_concurrency_copts = [ + "-Xfrontend", + "-strict-concurrency=targeted", +] # Targets @@ -140,7 +144,7 @@ swift_library( srcs = glob( ["Source/SwiftLintFramework/**/*.swift"], ), - copts = copts, # TODO: strict_concurrency_copts + copts = copts + targeted_concurrency_copts, module_name = "SwiftLintFramework", visibility = ["//visibility:public"], deps = [ diff --git a/Package.swift b/Package.swift index 8e5ca12135..a6fcd21b72 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,8 @@ let swiftFeatures: [SwiftSetting] = [ .enableUpcomingFeature("ForwardTrailingClosures"), .enableUpcomingFeature("ImplicitOpenExistentials"), ] -let strictConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency")] +let strictConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency=complete")] +let targetedConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency=targeted")] let swiftLintPluginDependencies: [Target.Dependency] @@ -57,7 +58,7 @@ let package = Package( "SwiftLintExtraRules", "CollectionConcurrencyKit", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + targetedConcurrency ), .plugin( name: "SwiftLintBuildToolPlugin", From 2405508324194dc99e91b56e7e135addb281deb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 4 Jan 2025 21:46:04 +0100 Subject: [PATCH 149/260] Improve wording of OSS check output (#5938) --- tools/oss-check | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/oss-check b/tools/oss-check index d84753f7e2..3bfdcef8be 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -21,16 +21,16 @@ require 'erb' } OptionParser.new do |opts| - opts.on('--branch BRANCH', "compares the performance of BRANCH against 'main'") do |branch| + opts.on('--branch BRANCH', "Compares the performance of BRANCH against `main`") do |branch| @options[:branch] = branch end - opts.on('--iterations N', Integer, 'iterates lint N times on each repositories') do |iterations| + opts.on('--iterations N', Integer, 'Runs linting N times on each repository') do |iterations| @options[:iterations] = iterations end - opts.on('--skip-clean', 'skip cleaning on completion') do |skip_clean| + opts.on('--skip-clean', 'Skip cleaning upon completion') do |skip_clean| @options[:skip_clean] = skip_clean end - opts.on('--force', 'run oss-check even if binaries are equal') do |force| + opts.on('--force', 'Run oss-check even if binaries are equal') do |force| @options[:force] = force end opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v| @@ -77,8 +77,8 @@ class Repo faster_slower = 'slower' percent_change *= -1 end - "Linting #{self} with this PR took #{@branch_duration}s " \ - "vs #{@main_duration}s on main (#{percent_change.to_i}\% #{faster_slower})." + "Linting #{self} with this PR took #{@branch_duration} s " \ + "vs #{@main_duration} s on `main` (#{percent_change.to_i}\% #{faster_slower})." end end @@ -235,7 +235,7 @@ def diff_and_report_changes_to_danger @repos.each do |repo| if repo.main_exit_value != repo.branch_exit_value warn "This PR changed the exit value from #{repo.main_exit_value} to #{repo.branch_exit_value} when " \ - "running on #{repo.name}." + "running in #{repo.name}." # If the exit value changed, don't show the fixes or regressions for this # repo because it's likely due to a crash, and all violations would be noisy next @@ -356,7 +356,7 @@ unless @options[:force] full_version_main = `#{@working_dir}/builds/swiftlint-main version --verbose` if full_version_branch == full_version_main - message "Skipping OSSCheck because SwiftLint hasn't changed compared to 'main'." + message "Skipping OSS check because SwiftLint hasn't changed compared to `main`." # Clean up clean_up unless @options[:skip_clean] exit From 6ee820da152cc99e48ae4dcab05b5360d6a7024b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 5 Jan 2025 12:15:59 +0100 Subject: [PATCH 150/260] Update Brave repository URL --- tools/oss-check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/oss-check b/tools/oss-check index 3bfdcef8be..eded479d29 100755 --- a/tools/oss-check +++ b/tools/oss-check @@ -315,7 +315,7 @@ end @repos = [ Repo.new('Aerial', 'JohnCoates/Aerial'), Repo.new('Alamofire', 'Alamofire/Alamofire'), - Repo.new('Brave', 'brave/brave-ios', true), + Repo.new('Brave', 'brave/brave-core', false, 'included: ios/brave-ios'), Repo.new('DuckDuckGo', 'duckduckgo/iOS'), Repo.new('Firefox', 'mozilla-mobile/firefox-ios'), Repo.new('Kickstarter', 'kickstarter/ios-oss'), From af0565adb06f0d95be352f07e41e2501568f09c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 6 Jan 2025 14:07:50 +0100 Subject: [PATCH 151/260] Add Swift Package Index configuration --- .spi.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .spi.yml diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 0000000000..f1abadddac --- /dev/null +++ b/.spi.yml @@ -0,0 +1,3 @@ +version: 1 +external_links: + documentation: "https://realm.github.io/SwiftLint" From 8cde33a4929803374adabf14ee83dea2e17eeefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 6 Jan 2025 14:15:09 +0100 Subject: [PATCH 152/260] Add badges created by Swift Package Index --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7fee6e124e..f9a1c1920c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ SwiftLint hooks into [Clang](http://clang.llvm.org) and [AST](http://clang.llvm.org/docs/IntroductionToTheClangAST.html) representation of your source files for more accurate results. +[![Supported Swift Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Frealm%2FSwiftLint%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/realm/SwiftLint) +[![Supported Platforms](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Frealm%2FSwiftLint%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/realm/SwiftLint) [![Azure Build Status](https://dev.azure.com/jpsim/SwiftLint/_apis/build/status/realm.SwiftLint?branchName=main)](https://dev.azure.com/jpsim/SwiftLint/_build/latest?definitionId=4?branchName=main) [![Buildkite Build Status](https://badge.buildkite.com/e2a5bc32c347e76e2793e4c5764a5f42bcd42bbe32f79c3a53.svg?branch=main)](https://buildkite.com/swiftlint/swiftlint) From a5c9319e131a42ffdee663b891d0b77af996051d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 6 Jan 2025 18:28:20 +0100 Subject: [PATCH 153/260] Fix warnings in Linux build (#5940) --- Source/SwiftLintCore/Extensions/QueuedPrint.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/SwiftLintCore/Extensions/QueuedPrint.swift b/Source/SwiftLintCore/Extensions/QueuedPrint.swift index bc898c5b5d..4c8d12700a 100644 --- a/Source/SwiftLintCore/Extensions/QueuedPrint.swift +++ b/Source/SwiftLintCore/Extensions/QueuedPrint.swift @@ -1,5 +1,5 @@ -import Dispatch -import Foundation +@preconcurrency import Dispatch +@preconcurrency import Foundation private let outputQueue: DispatchQueue = { let queue = DispatchQueue( From fb4b8e0ef223f185e6708b06cdf6a372ba643d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 6 Jan 2025 18:47:49 +0100 Subject: [PATCH 154/260] Update copyright holders This line is not binding anyway as all code still belongs to the original authors. However, all contributors shall be credited equally. --- LICENSE | 2 +- README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index dae3569e96..0b64fb61db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2025 Realm Inc. +Copyright (c) 2025 The SwiftLint Contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f9a1c1920c..ec07285e0c 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,7 @@ of your source files for more accurate results. This project adheres to the [Contributor Covenant Code of Conduct](https://realm.io/conduct). -By participating, you are expected to uphold this code. Please report -unacceptable behavior to [info@realm.io](mailto:info@realm.io). +By participating, you are expected to uphold this code. > Switch Language: > [中文](https://github.com/realm/SwiftLint/blob/main/README_CN.md) From fc70594ad225d420eec5f40dc3c01847f9fbb64e Mon Sep 17 00:00:00 2001 From: Koichiro Ueki <43738558+Ueeek@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:22:10 +0900 Subject: [PATCH 155/260] Ignore TipKit's `#Rule` macro in `empty_count` rule (#5918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- CHANGELOG.md | 4 ++ .../Rules/Performance/EmptyCountRule.swift | 44 +++++++++++++++++++ .../EmptyCountRuleTests.swift | 4 ++ 3 files changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6538b218b1..8430f28289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,10 @@ #### Bug Fixes +* Ignore TipKit's `#Rule` macro in `empty_count` rule. + [Ueeek](https://github.com/Ueeek) + [#5883](https://github.com/realm/SwiftLint/issues/5883) + * Ignore super calls with trailing closures in `unneeded_override` rule. [SimplyDanny](https://github.com/SimplyDanny) [#5886](ttps://github.com/realm/SwiftLint/issues/5886) diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift index 3901237f2a..c1166ee473 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift @@ -20,6 +20,8 @@ struct EmptyCountRule: Rule { Example("[Int]().count == 0o07"), Example("discount == 0"), Example("order.discount == 0"), + Example("let rule = #Rule(Tips.Event(id: \"someTips\")) { $0.donations.count == 0 }"), + Example("#Rule(param1: \"param1\")", excludeFromDocumentation: true), ], triggeringExamples: [ Example("[Int]().↓count == 0"), @@ -32,6 +34,23 @@ struct EmptyCountRule: Rule { Example("[Int]().↓count == 0b00"), Example("[Int]().↓count == 0o00"), Example("↓count == 0"), + Example("#ExampleMacro { $0.list.↓count == 0 }"), + Example("#Rule { $0.donations.↓count == 0 }", excludeFromDocumentation: true), + Example( + "#Rule(param1: \"param1\", param2: \"param2\") { $0.donations.↓count == 0 }", + excludeFromDocumentation: true + ), + Example( + "#Rule(param1: \"param1\") { $0.donations.↓count == 0 } closure2: { doSomething() }", + excludeFromDocumentation: true + ), + Example("#Rule(param1: \"param1\") { return $0.donations.↓count == 0 }", excludeFromDocumentation: true), + Example(""" + #Rule(param1: "param1") { + doSomething() + return $0.donations.↓count == 0 + } + """, excludeFromDocumentation: true), ], corrections: [ Example("[].↓count == 0"): @@ -62,6 +81,10 @@ struct EmptyCountRule: Rule { Example("isEmpty && [Int]().isEmpty"), Example("[Int]().count != 3 && [Int]().↓count != 0 || ↓count == 0 && [Int]().count > 2"): Example("[Int]().count != 3 && ![Int]().isEmpty || isEmpty && [Int]().count > 2"), + Example("#ExampleMacro { $0.list.↓count == 0 }"): + Example("#ExampleMacro { $0.list.isEmpty }"), + Example("#Rule(param1: \"param1\") { return $0.donations.↓count == 0 }"): + Example("#Rule(param1: \"param1\") { return $0.donations.isEmpty }"), ] ) } @@ -77,6 +100,10 @@ private extension EmptyCountRule { violations.append(position) } } + + override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind { + node.isTipsRuleMacro ? .skipChildren : .visitChildren + } } final class Rewriter: ViolationsSyntaxRewriter { @@ -105,6 +132,14 @@ private extension EmptyCountRule { } return super.visit(node) } + + override func visit(_ node: MacroExpansionExprSyntax) -> ExprSyntax { + if node.isTipsRuleMacro { + ExprSyntax(node) + } else { + super.visit(node) + } + } } } @@ -137,6 +172,15 @@ private extension TokenSyntax { } } +private extension MacroExpansionExprSyntax { + var isTipsRuleMacro: Bool { + macroName.text == "Rule" && + additionalTrailingClosures.isEmpty && + arguments.count == 1 && + trailingClosure.map { $0.statements.onlyElement?.item.is(ReturnStmtSyntax.self) == false } ?? false + } +} + private extension ExprSyntaxProtocol { var negated: ExprSyntax { ExprSyntax(PrefixOperatorExprSyntax(operator: .prefixOperator("!"), expression: self)) diff --git a/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift b/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift index 7df9e49108..75d50cf456 100644 --- a/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift +++ b/Tests/BuiltInRulesTests/EmptyCountRuleTests.swift @@ -15,6 +15,7 @@ final class EmptyCountRuleTests: SwiftLintTestCase { Example("discount == 0\n"), Example("order.discount == 0\n"), Example("count == 0\n"), + Example("let rule = #Rule(Tips.Event(id: \"someTips\")) { $0.donations.isEmpty }"), ] let triggeringExamples = [ Example("[Int]().↓count == 0\n"), @@ -24,6 +25,7 @@ final class EmptyCountRuleTests: SwiftLintTestCase { Example("[Int]().↓count == 0x00_00\n"), Example("[Int]().↓count == 0b00\n"), Example("[Int]().↓count == 0o00\n"), + Example("#ExampleMacro { $0.list.↓count == 0 }"), ] let corrections = [ @@ -55,6 +57,8 @@ final class EmptyCountRuleTests: SwiftLintTestCase { Example("count == 0 && [Int]().isEmpty"), Example("[Int]().count != 3 && [Int]().↓count != 0 || count == 0 && [Int]().count > 2"): Example("[Int]().count != 3 && ![Int]().isEmpty || count == 0 && [Int]().count > 2"), + Example("#ExampleMacro { $0.list.↓count == 0 }"): + Example("#ExampleMacro { $0.list.isEmpty }"), ] let description = EmptyCountRule.description From 440f6d1cfe857c963671e3107049d7776115e82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 7 Jan 2025 20:52:49 +0100 Subject: [PATCH 156/260] Set compile options for test of extra rules (#5941) --- Tests/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/BUILD b/Tests/BUILD index 57ef810106..cc2d407ea7 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -208,6 +208,7 @@ swift_library( deps = [ ":TestHelpers", ], + copts = copts, ) swift_test( From 0f6f78e8357c4b39810d87e39e4fa1fa0681eb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 7 Jan 2025 20:53:17 +0100 Subject: [PATCH 157/260] Update Bazel publishing templates * Require Swift 6 in Linux build. * Keep warnings as long as an older Swift compiler is used in macOS builds. --- .bcr/patches/no-warnings-as-errors.patch | 10 ++++++++++ .bcr/presubmit.yml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .bcr/patches/no-warnings-as-errors.patch diff --git a/.bcr/patches/no-warnings-as-errors.patch b/.bcr/patches/no-warnings-as-errors.patch new file mode 100644 index 0000000000..e0e6f96558 --- /dev/null +++ b/.bcr/patches/no-warnings-as-errors.patch @@ -0,0 +1,10 @@ +--- BUILD ++++ BUILD +@@ -21,7 +21,6 @@ config_setting( + ) + + copts = [ +- "-warnings-as-errors", + "-enable-upcoming-feature", + "ExistentialAny", + "-enable-upcoming-feature", diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 0ca2eb510b..8186218f3d 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -5,7 +5,7 @@ tasks: bazel: 7.x environment: CC: "clang" - SWIFT_VERSION: "5.10" + SWIFT_VERSION: "6.0.3" SWIFT_HOME: "$HOME/swift-$SWIFT_VERSION" PATH: "$PATH:$SWIFT_HOME/usr/bin" shell_commands: From a12a1a19703721a7553428224d1540985d808e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 7 Jan 2025 20:59:23 +0100 Subject: [PATCH 158/260] Update Xcode project generation (#5942) --- bazel/BUILD | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bazel/BUILD b/bazel/BUILD index b9b506b85e..a386e6740b 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -20,21 +20,25 @@ xcodeproj( ], ), test_action = xcode_schemes.test_action([ + "//Tests:BuiltInRulesTests", "//Tests:CLITests", - "//Tests:SwiftLintFrameworkTests", + "//Tests:ExtraRulesTests", + "//Tests:FrameworkTests", "//Tests:GeneratedTests", "//Tests:IntegrationTests", - "//Tests:ExtraRulesTests", + "//Tests:MacrosTests", ]), ), ], top_level_targets = [ "//:swiftlint", + "//Tests:BuiltInRulesTests", "//Tests:CLITests", - "//Tests:SwiftLintFrameworkTests", + "//Tests:ExtraRulesTests", + "//Tests:FrameworkTests", "//Tests:GeneratedTests", "//Tests:IntegrationTests", - "//Tests:ExtraRulesTests", + "//Tests:MacrosTests", ], ) From f796fee24138931b19766f9bb73dd0ce9325a303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 7 Jan 2025 22:19:25 +0100 Subject: [PATCH 159/260] Enable strict concurrency checks in test code (#5943) --- Package.swift | 16 ++++++------ Source/SwiftLintCore/Models/Issue.swift | 2 +- Tests/BUILD | 26 +++++++++++++------ ...licitTypeInterfaceConfigurationTests.swift | 7 ++--- .../IndentationWidthRuleTests.swift | 14 +++++----- ...ultilineParametersConfigurationTests.swift | 6 ++--- .../NoEmptyBlockConfigurationTests.swift | 7 ++--- Tests/FrameworkTests/BaselineTests.swift | 10 ++++--- .../RuleConfigurationDescriptionTests.swift | 20 +++++++------- 9 files changed, 60 insertions(+), 48 deletions(-) diff --git a/Package.swift b/Package.swift index a6fcd21b72..6576d12e9d 100644 --- a/Package.swift +++ b/Package.swift @@ -124,14 +124,14 @@ let package = Package( exclude: [ "Resources", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "CLITests", dependencies: [ "SwiftLintFramework", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "ExtraRulesTests", @@ -139,7 +139,7 @@ let package = Package( "SwiftLintFramework", "TestHelpers", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "FrameworkTests", @@ -151,7 +151,7 @@ let package = Package( exclude: [ "Resources", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "GeneratedTests", @@ -159,7 +159,7 @@ let package = Package( "SwiftLintFramework", "TestHelpers", ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .testTarget( name: "IntegrationTests", @@ -170,7 +170,7 @@ let package = Package( exclude: [ "default_rule_configurations.yml" ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + targetedConcurrency // Set to strict once SwiftLintFramework is updated ), .testTarget( name: "MacroTests", @@ -178,7 +178,7 @@ let package = Package( "SwiftLintCoreMacros", .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), ], - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), .target( name: "TestHelpers", @@ -186,7 +186,7 @@ let package = Package( "SwiftLintFramework" ], path: "Tests/TestHelpers", - swiftSettings: swiftFeatures + swiftSettings: swiftFeatures + strictConcurrency ), ] ) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index 9c120d1542..830911c8a9 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -94,7 +94,7 @@ public enum Issue: LocalizedError, Equatable { /// /// - returns: The collected messages produced while running the code in the runner. @MainActor - static func captureConsole(runner: () throws -> Void) async rethrows -> String { + static func captureConsole(runner: @Sendable () throws -> Void) async rethrows -> String { actor Console { static var content = "" } diff --git a/Tests/BUILD b/Tests/BUILD index cc2d407ea7..b73aa578d0 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -3,6 +3,7 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library", "swift_test") exports_files(["BUILD"]) copts = [ + "-warnings-as-errors", "-enable-upcoming-feature", "ExistentialAny", "-enable-upcoming-feature", @@ -15,6 +16,15 @@ copts = [ "ImplicitOpenExistentials", ] +strict_concurrency_copts = [ + "-Xfrontend", + "-strict-concurrency=complete", +] +targeted_concurrency_copts = [ + "-Xfrontend", + "-strict-concurrency=targeted", +] + # CLITests swift_library( @@ -26,7 +36,7 @@ swift_library( deps = [ "//:SwiftLintFramework", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( @@ -46,7 +56,7 @@ swift_library( "//:SwiftLintCoreMacrosLib", "@SwiftSyntax//:SwiftSyntaxMacrosTestSupport_opt", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( @@ -66,7 +76,7 @@ swift_library( deps = [ "//:SwiftLintFramework", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) # BuiltInRulesTests @@ -87,7 +97,7 @@ swift_library( deps = [ ":TestHelpers", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( @@ -117,7 +127,7 @@ swift_library( deps = [ ":TestHelpers", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( @@ -140,7 +150,7 @@ swift_library( deps = [ ":TestHelpers", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( @@ -169,7 +179,7 @@ swift_library( deps = [ ":TestHelpers", ], - copts = copts, + copts = copts + targeted_concurrency_copts, # Set to strict once SwiftLintFramework is updated ) swift_test( @@ -208,7 +218,7 @@ swift_library( deps = [ ":TestHelpers", ], - copts = copts, + copts = copts + strict_concurrency_copts, ) swift_test( diff --git a/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift index ee1d9b7a60..6108180ba8 100644 --- a/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift @@ -24,11 +24,12 @@ final class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase { XCTAssertTrue(config.allowRedundancy) } - @MainActor func testInvalidKeyInCustomConfiguration() async throws { - var config = ExplicitTypeInterfaceConfiguration() try await AsyncAssertEqual( - try await Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, + try await Issue.captureConsole { + var config = ExplicitTypeInterfaceConfiguration() + try config.apply(configuration: ["invalidKey": "error"]) + }, "warning: Configuration for 'explicit_type_interface' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift index c8ae98abab..498745be78 100644 --- a/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift +++ b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift @@ -4,18 +4,20 @@ import TestHelpers import XCTest final class IndentationWidthRuleTests: SwiftLintTestCase { - @MainActor func testInvalidIndentation() async throws { - var testee = IndentationWidthConfiguration() - let defaultValue = testee.indentationWidth + let defaultValue = IndentationWidthConfiguration().indentationWidth for indentation in [0, -1, -5] { try await AsyncAssertEqual( - try await Issue.captureConsole { try testee.apply(configuration: ["indentation_width": indentation]) }, + try await Issue.captureConsole { + var testee = IndentationWidthConfiguration() + try testee.apply(configuration: ["indentation_width": indentation]) + + // Value remains the default. + XCTAssertEqual(testee.indentationWidth, defaultValue) + }, "warning: Invalid configuration for 'indentation_width' rule. Falling back to default." ) - // Value remains the default. - XCTAssertEqual(testee.indentationWidth, defaultValue) } } diff --git a/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift index 12049483d0..6d19466b94 100644 --- a/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift @@ -6,10 +6,9 @@ import XCTest final class MultilineParametersConfigurationTests: SwiftLintTestCase { func testInvalidMaxNumberOfSingleLineParameters() async throws { for maxNumberOfSingleLineParameters in [0, -1] { - var config = MultilineParametersConfiguration() - try await AsyncAssertEqual( try await Issue.captureConsole { + var config = MultilineParametersConfiguration() try config.apply( configuration: ["max_number_of_single_line_parameters": maxNumberOfSingleLineParameters] ) @@ -23,10 +22,9 @@ final class MultilineParametersConfigurationTests: SwiftLintTestCase { } func testInvalidMaxNumberOfSingleLineParametersWithSingleLineEnabled() async throws { - var config = MultilineParametersConfiguration() - try await AsyncAssertEqual( try await Issue.captureConsole { + var config = MultilineParametersConfiguration() try config.apply( configuration: ["max_number_of_single_line_parameters": 2, "allows_single_line": false] ) diff --git a/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift index 96f287145d..f555bbbcb7 100644 --- a/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift @@ -22,11 +22,12 @@ final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { XCTAssertEqual(config.enabledBlockTypes, Set([.initializerBodies, .statementBlocks, .closureBlocks])) } - @MainActor func testInvalidKeyInCustomConfiguration() async throws { - var config = NoEmptyBlockConfiguration() try await AsyncAssertEqual( - try await Issue.captureConsole { try config.apply(configuration: ["invalidKey": "error"]) }, + try await Issue.captureConsole { + var config = NoEmptyBlockConfiguration() + try config.apply(configuration: ["invalidKey": "error"]) + }, "warning: Configuration for 'no_empty_block' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/FrameworkTests/BaselineTests.swift b/Tests/FrameworkTests/BaselineTests.swift index aefcca06aa..f341d0012d 100644 --- a/Tests/FrameworkTests/BaselineTests.swift +++ b/Tests/FrameworkTests/BaselineTests.swift @@ -47,7 +47,9 @@ final class BaselineTests: XCTestCase { DirectReturnRule.description, ] - private static var currentDirectoryPath: String? + private actor CurrentDirectoryHolder { + static var currentDirectoryPath: String? + } private static func violations(for filePath: String?) -> [StyleViolation] { ruleDescriptions.violations(for: filePath) @@ -59,14 +61,14 @@ final class BaselineTests: XCTestCase { override static func setUp() { super.setUp() - currentDirectoryPath = FileManager.default.currentDirectoryPath + CurrentDirectoryHolder.currentDirectoryPath = FileManager.default.currentDirectoryPath XCTAssertTrue(FileManager.default.changeCurrentDirectoryPath(temporaryDirectoryPath)) } override static func tearDown() { - if let currentDirectoryPath { + if let currentDirectoryPath = CurrentDirectoryHolder.currentDirectoryPath { XCTAssertTrue(FileManager.default.changeCurrentDirectoryPath(currentDirectoryPath)) - self.currentDirectoryPath = nil + CurrentDirectoryHolder.currentDirectoryPath = nil } super.tearDown() } diff --git a/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift index 8e15565c37..ddda2f436d 100644 --- a/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift +++ b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift @@ -490,31 +490,29 @@ final class RuleConfigurationDescriptionTests: SwiftLintTestCase { XCTAssertEqual(configuration.nestedSeverityLevels, SeverityLevelsConfiguration(warning: 6, error: 7)) } - @MainActor func testDeprecationWarning() async throws { - var configuration = TestConfiguration() - try await AsyncAssertEqual( - try await Issue.captureConsole { try configuration.apply(configuration: ["set": [6, 7]]) }, + try await Issue.captureConsole { + var configuration = TestConfiguration() + try configuration.apply(configuration: ["set": [6, 7]]) + }, "warning: Configuration option 'set' in 'my_rule' rule is deprecated. Use the option 'other_opt' instead." ) } - @MainActor func testNoDeprecationWarningIfNoDeprecatedPropertySet() async throws { - var configuration = TestConfiguration() - try await AsyncAssertTrue( - try await Issue.captureConsole { try configuration.apply(configuration: ["flag": false]) }.isEmpty + try await Issue.captureConsole { + var configuration = TestConfiguration() + try configuration.apply(configuration: ["flag": false]) + }.isEmpty ) } - @MainActor func testInvalidKeys() async throws { - var configuration = TestConfiguration() - try await AsyncAssertEqual( try await Issue.captureConsole { + var configuration = TestConfiguration() try configuration.apply(configuration: [ "severity": "error", "warning": 3, From c08e3e8a8bd867e4039a630dc8ef70f274b1cbe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 7 Jan 2025 23:48:04 +0100 Subject: [PATCH 160/260] Prohibit access of package-visible symbols from plugins (#5944) --- Package.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 6576d12e9d..8e3fd077f6 100644 --- a/Package.swift +++ b/Package.swift @@ -63,7 +63,8 @@ let package = Package( .plugin( name: "SwiftLintBuildToolPlugin", capability: .buildTool(), - dependencies: swiftLintPluginDependencies + dependencies: swiftLintPluginDependencies, + packageAccess: false ), .plugin( name: "SwiftLintCommandPlugin", @@ -75,7 +76,8 @@ let package = Package( ), ] ), - dependencies: swiftLintPluginDependencies + dependencies: swiftLintPluginDependencies, + packageAccess: false ), .target( name: "SwiftLintCore", From 5777bab4768c9ec7f4d1185134cb291842380e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 8 Jan 2025 19:36:50 +0100 Subject: [PATCH 161/260] Avoid condition that's always true --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fd25557deb..73401c9c7e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Docker tag - if: github.event_name != 'push' && ${{ inputs.tag != 'latest' }} + if: github.event_name != 'push' && inputs.tag != 'latest' run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - name: Use default Docker tag if: github.event_name == 'push' From 9b34d3c3f6808677a02c839f148e783567f81780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 19:23:46 +0100 Subject: [PATCH 162/260] Trigger release actions on tag creation --- .github/workflows/release.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f6acc511a..0ff05c09a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,9 @@ name: Release on: - release: - types: [released] + push: + tags: + - '0.[0-9]+.[0-9]+' jobs: dispatch-plugins: @@ -13,6 +14,9 @@ jobs: - name: Parse checksum id: parse_checksum run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT + - name: Extract release title + id: extract_title + run: echo "title=$(git tag -l --format='%(subject)' ${{ github.ref_name }})" >> $GITHUB_OUTPUT - name: Dispatch release of plugins package uses: peter-evans/repository-dispatch@v3 with: @@ -21,15 +25,15 @@ jobs: event-type: swiftlint-release client-payload: |- { - "title": "${{ github.event.release.name }}", - "tag": "${{ github.event.release.tag_name }}", + "title": "${{ steps.extract_title.outputs.title }}", + "tag": "${{ github.ref_name }}", "checksum": "${{ steps.parse_checksum.outputs.checksum }}" } trigger-docker: uses: ./.github/workflows/docker.yml secrets: inherit with: - tag: ${{ github.event.release.tag_name }} + tag: ${{ github.ref_name }} upload-docker: needs: trigger-docker runs-on: ubuntu-20.04 @@ -46,8 +50,7 @@ jobs: - name: Retrieve author in uppercase id: retrieve_author run: | - author=${{ github.event.release.author.login }} - AUTHOR=$(echo $author | tr '[:lower:]' '[:upper:]') + AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Deploy to CocoaPods run: make pod_publish From c8ae928776158bbf49b034e41c03ea5448ee7d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 20:15:24 +0100 Subject: [PATCH 163/260] Skip formula bump temporarily --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index c65cf32b69..e80c6f8da6 100644 --- a/Makefile +++ b/Makefile @@ -200,7 +200,6 @@ endif git push origin HEAD git push origin $(NEW_VERSION) ./tools/create-github-release.sh "$(NEW_VERSION)" - make formula_bump ./tools/add-new-changelog-section.sh git commit -a -m "Add new changelog section" git push origin HEAD From ffa1979c880c77bd4c58a2355f8ef7286e445af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 20:43:16 +0100 Subject: [PATCH 164/260] Prepare for 0.58.0 release --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 8e3fd077f6..26754933cb 100644 --- a/Package.swift +++ b/Package.swift @@ -194,11 +194,10 @@ let package = Package( ) #if os(macOS) -// TODO: in the next release the artifactbundle is not suffixed with "-macos" package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary-macos.artifactbundle.zip", + url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary.artifactbundle.zip", checksum: "c88bf3e5bc1326d8ca66bc3f9eae786f2094c5172cd70b26b5f07686bb883899" ) ) From 04201c6af5590435ac1af481e855942ee145a57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 20:51:51 +0100 Subject: [PATCH 165/260] Adapt version template --- tools/Version.swift.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Version.swift.template b/tools/Version.swift.template index 685d37697e..30e8a86a59 100644 --- a/tools/Version.swift.template +++ b/tools/Version.swift.template @@ -1,5 +1,5 @@ /// A type describing the SwiftLint version. -public struct Version: VersionComparable { +public struct Version: VersionComparable, Sendable { /// The string value for this version. public let value: String From 1d3ebc8034e9bc9fd01e56ec960baea2fc19f3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 23:02:26 +0100 Subject: [PATCH 166/260] Define release process in a workflow --- .github/workflows/docker.yml | 12 +-- .github/workflows/release.yml | 153 +++++++++++++++++++++++++++------- 2 files changed, 131 insertions(+), 34 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 73401c9c7e..ac3f244816 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,13 +6,10 @@ on: - main workflow_call: inputs: - tag: - description: 'Docker tag' + sha: + description: 'Git commit SHA' required: true type: string - default: 'latest' - workflow_dispatch: - inputs: tag: description: 'Docker tag' required: true @@ -24,6 +21,11 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + if: github.event_name != 'push' && inputs.tag != 'latest' + with: + ref: ${{ inputs.sha }} + - uses: actions/checkout@v4 + if: github.event_name == 'push' - name: Set Docker tag if: github.event_name != 'push' && inputs.tag != 'latest' run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ff05c09a6..eee953363c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,52 +1,124 @@ name: Release on: - push: - tags: - - '0.[0-9]+.[0-9]+' + workflow_dispatch: + inputs: + version: + description: 'Release version' + required: true + type: string + title: + description: 'Release title' + required: true + type: string jobs: - dispatch-plugins: - runs-on: ubuntu-latest + create-release: + runs-on: macOS-latest + outputs: + pre_release_sha: ${{ steps.pre_release.outputs.pre_release_sha }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Parse checksum - id: parse_checksum - run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT - - name: Extract release title - id: extract_title - run: echo "title=$(git tag -l --format='%(subject)' ${{ github.ref_name }})" >> $GITHUB_OUTPUT - - name: Dispatch release of plugins package - uses: peter-evans/repository-dispatch@v3 + - name: Update changelog + run: "sed -i '' 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" + - name: Update built-in versions + run: | + sed 's/__VERSION__/${{ inputs.version }}/g' tools/Version.swift.template > Source/SwiftLintFramework/Models/Version.swift + sed -e '3s/.*/ version = "${{ inputs.version }}",/' -i '' MODULE.bazel + - name: Configure author + run: | + git config --local user.name SimplyDanny + git config --local user.email danny.moesch@icloud.com + - name: Commit changes + id: pre_release + run: | + git commit -a -m "Prepare ${{ inputs.version }} release" + echo "pre_release_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + git push origin HEAD + - uses: ./.github/workflows/docker.yml with: - token: ${{ secrets.SIMPLYDANNY_PLUGINS_SYNC }} - repository: SimplyDanny/SwiftLintPlugins - event-type: swiftlint-release - client-payload: |- - { - "title": "${{ steps.extract_title.outputs.title }}", - "tag": "${{ github.ref_name }}", - "checksum": "${{ steps.parse_checksum.outputs.checksum }}" - } - trigger-docker: - uses: ./.github/workflows/docker.yml - secrets: inherit - with: - tag: ${{ github.ref_name }} + sha: ${{ steps.pre_release.outputs.pre_release_sha }} + tag: ${{ inputs.version }} + - name: Create SPM artifact bundle + run: make spm_artifactbundle + - name: Update binary target in Swift package + run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" + - name: Create tag and release commit + run: | + git commit -a -m "Release ${{ inputs.version }}" + git tag -a ${{ inputs.version }} -m "${{ inputs.title }}" + git push origin HEAD + git push origin ${{ inputs.version }} + - name: Create release + run: | + release_notes=$(mktemp) + ./tools/generate-release-notes.sh "${{ inputs.version }}" > "$release_notes" + gh release create ${{ inputs.version }} --title ${{ inputs.title }} -F "$release_notes" --draft + rm "$release_notes" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload artifact bundle to release + run: gh release upload ${{ inputs.version }} SwiftLintBinary.artifactbundle.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} upload-docker: - needs: trigger-docker - runs-on: ubuntu-20.04 + needs: create-release + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} - name: Upload binary to existing release run: make zip_linux_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + upload-package: + needs: create-release + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} + - name: Create package + run: make package + - name: Upload package to existing release + run: gh release upload ${{ inputs.version }} SwiftLint.pkg + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + upload-bazel: + needs: create-release + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} + - name: Create Bazel release + run: make bazel_release + - name: Upload Bazel release to existing release + run: gh release upload ${{ inputs.version }} bazel.tar.gz bazel.tar.gz.sha256 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + upload-portable-zip: + needs: create-release + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} + - name: Create portable ZIP + run: make portable_zip + - name: Upload portable ZIP to existing release + run: gh release upload ${{ inputs.version }} portable_swiftlint.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-pod: + needs: create-release runs-on: macOS-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} - name: Retrieve author in uppercase id: retrieve_author run: | @@ -56,3 +128,26 @@ jobs: run: make pod_publish env: COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} + dispatch-plugins: + needs: create-release + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} + - name: Parse checksum + id: parse_checksum + run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT + - name: Dispatch release of plugins package + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.SIMPLYDANNY_PLUGINS_SYNC }} + repository: SimplyDanny/SwiftLintPlugins + event-type: swiftlint-release + client-payload: |- + { + "title": "${{ inputs.title }}", + "tag": "${{ inputs.version }}", + "checksum": "${{ steps.parse_checksum.outputs.checksum }}" + } From 2fa6b43df3e5d61b21abc9132d081abdcffa75dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 10 Jan 2025 21:41:08 +0100 Subject: [PATCH 167/260] Create release branch --- .github/workflows/release.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eee953363c..d1c7621cc3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,11 @@ on: description: 'Release title' required: true type: string + branch: + description: 'Release branch' + required: false + type: string + default: 'main' jobs: create-release: @@ -20,6 +25,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Create release branch + if: inputs.branch != 'main' + run: git checkout -b ${{ inputs.branch }} - name: Update changelog run: "sed -i '' 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions From 5e13cf99158869a6f4037ef535816538ae0a314a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 10 Jan 2025 22:08:42 +0100 Subject: [PATCH 168/260] Call workflow in separate job --- .github/workflows/release.yml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1c7621cc3..0b887cedb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,8 +18,8 @@ on: default: 'main' jobs: - create-release: - runs-on: macOS-latest + prepare-release: + runs-on: ubuntu-latest outputs: pre_release_sha: ${{ steps.pre_release.outputs.pre_release_sha }} steps: @@ -29,25 +29,39 @@ jobs: if: inputs.branch != 'main' run: git checkout -b ${{ inputs.branch }} - name: Update changelog - run: "sed -i '' 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" + run: "sed -i 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions run: | sed 's/__VERSION__/${{ inputs.version }}/g' tools/Version.swift.template > Source/SwiftLintFramework/Models/Version.swift - sed -e '3s/.*/ version = "${{ inputs.version }}",/' -i '' MODULE.bazel + sed -i -e '3s/.*/ version = "${{ inputs.version }}",/' MODULE.bazel - name: Configure author run: | - git config --local user.name SimplyDanny - git config --local user.email danny.moesch@icloud.com + git config --local user.name "Danny Mösch" + git config --local user.email "danny.moesch@icloud.com" - name: Commit changes id: pre_release run: | git commit -a -m "Prepare ${{ inputs.version }} release" echo "pre_release_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT git push origin HEAD - - uses: ./.github/workflows/docker.yml + build-docker: + needs: prepare-release + uses: ./.github/workflows/docker.yml + secrets: inherit + with: + sha: ${{ needs.prepare-release.outputs.pre_release_sha }} + tag: ${{ inputs.version }} + create-release: + needs: build-docker + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 with: - sha: ${{ steps.pre_release.outputs.pre_release_sha }} - tag: ${{ inputs.version }} + ref: ${{ inputs.branch }} + - name: Configure author + run: | + git config --local user.name "Danny Mösch" + git config --local user.email "danny.moesch@icloud.com" - name: Create SPM artifact bundle run: make spm_artifactbundle - name: Update binary target in Swift package From e15357364e70b9655386f75b7b6fa7d874437c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 10 Jan 2025 22:40:18 +0100 Subject: [PATCH 169/260] Specify specific Xcode version --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b887cedb1..eb3ea12166 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: tag: ${{ inputs.version }} create-release: needs: build-docker - runs-on: macOS-latest + runs-on: macOS-14 steps: - uses: actions/checkout@v4 with: @@ -64,6 +64,8 @@ jobs: git config --local user.email "danny.moesch@icloud.com" - name: Create SPM artifact bundle run: make spm_artifactbundle + env: + DEVELOPER_DIR: /Applications/Xcode_16.2.app - name: Update binary target in Swift package run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" - name: Create tag and release commit From 803243ca6fffe8f4c6df583be19cc2ab6544ef11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 10 Jan 2025 22:41:19 +0100 Subject: [PATCH 170/260] Upload artifact only in workflow --- .github/workflows/release.yml | 4 +++- Makefile | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb3ea12166..72c6514f8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,7 +94,9 @@ jobs: with: ref: ${{ inputs.version }} - name: Upload binary to existing release - run: make zip_linux_release + run: | + make zip_linux_release + gh release upload ${{ inputs.version }} swiftlint_linux.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} upload-package: diff --git a/Makefile b/Makefile index e80c6f8da6..7df1aefc5b 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ installables: build install "$(SWIFTLINT_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" installables_linux: build_linux - install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" + install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" prefix_install: build_with_disable_sandbox @@ -133,7 +133,6 @@ zip_linux_release: build_linux cp -f "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TMP_FOLDER)/swiftlint" cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" (cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip" - gh release upload "$(VERSION_STRING)" swiftlint_linux.zip package: build $(eval PACKAGE_ROOT := $(shell mktemp -d)) From 7421e24fc281d73d4c37550f95054751b4fc0f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 00:04:53 +0100 Subject: [PATCH 171/260] Use action to extract binary from Docker image --- .github/workflows/release.yml | 10 ++++++++++ Makefile | 12 ++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72c6514f8d..d234d5c11a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,6 +62,16 @@ jobs: run: | git config --local user.name "Danny Mösch" git config --local user.email "danny.moesch@icloud.com" + - name: Create build folders + run: mkdir -p .build/linux + - name: Extract binary from Docker image + uses: shrink/actions-docker-extract@v3 + with: + image: ghcr.io/realm/swiftlint:${{ inputs.version }} + path: /usr/bin/swiftlint + destination: .build/linux/swiftlint_linux_amd64 + - name: Make binary executable + run: chmod +x .build/linux/swiftlint_linux_amd64 - name: Create SPM artifact bundle run: make spm_artifactbundle env: diff --git a/Makefile b/Makefile index 7df1aefc5b..f3a8eac0fd 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ OUTPUT_PACKAGE=SwiftLint.pkg VERSION_STRING=$(shell ./tools/get-version) -.PHONY: all clean build build_linux install package test uninstall docs +.PHONY: all clean build install package test uninstall docs all: build @@ -65,7 +65,7 @@ analyze_autocorrect: write_xcodebuild_log clean: rm -f "$(OUTPUT_PACKAGE)" rm -rf "$(TEMPORARY_FOLDER)" - rm -rf rule_docs/ docs/ + rm -rf rule_docs/ docs/ .build/ rm -f ./*.{zip,pkg} bazel.tar.gz bazel.tar.gz.sha256 swift package clean @@ -80,7 +80,7 @@ build: chmod +w "$(SWIFTLINT_EXECUTABLE)" strip -rSTX "$(SWIFTLINT_EXECUTABLE)" -build_linux: +$(SWIFTLINT_EXECUTABLE_LINUX_AMD64): mkdir -p "$(SWIFTLINT_EXECUTABLE_LINUX_PARENT)" docker run --platform linux/amd64 "ghcr.io/realm/swiftlint:$(VERSION_STRING)" cat /usr/bin/swiftlint > "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" chmod +x "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" @@ -100,7 +100,7 @@ installables: build install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" -installables_linux: build_linux +installables_linux: $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" @@ -122,13 +122,13 @@ spm_artifactbundle: installables installables_linux cp -f "$(LICENSE_PATH)" "$(ARTIFACT_BUNDLE_PATH)" (cd "$(TEMPORARY_FOLDER)"; zip -yr - "SwiftLintBinary.artifactbundle") > "./SwiftLintBinary.artifactbundle.zip" -zip_linux: docker_image build_linux +zip_linux: docker_image $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) $(eval TMP_FOLDER := $(shell mktemp -d)) cp -f $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) "$(TMP_FOLDER)/swiftlint" cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" (cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip" -zip_linux_release: build_linux +zip_linux_release: $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) $(eval TMP_FOLDER := $(shell mktemp -d)) cp -f "$(SWIFTLINT_EXECUTABLE_LINUX_AMD64)" "$(TMP_FOLDER)/swiftlint" cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" From f54840f8ba16443ce2da674b56da3d273597b6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 13:11:00 +0100 Subject: [PATCH 172/260] Build Linux and macOS binaries in parallel and upload them --- .github/workflows/docker.yml | 8 +++++ .github/workflows/release.yml | 55 ++++++++++++++++++++--------------- Makefile | 2 +- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ac3f244816..1ec17e7e86 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -48,3 +48,11 @@ jobs: with: push: true tags: ghcr.io/${{ env.REPOSITORY_LC }}:${{ env.DOCKER_TAG }} + platforms: linux/amd64 + outputs: type=local,dest=artifacts + - name: Upload binary artifact + uses: actions/upload-artifact@v4 + with: + name: swiftlint_linux_amd64 + path: artifacts/linux_amd64/workspace/swiftlint + retention-days: 2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d234d5c11a..6f3d28b768 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,9 @@ on: type: string default: 'main' +env: + DEVELOPER_DIR: /Applications/Xcode_16.2.app + jobs: prepare-release: runs-on: ubuntu-latest @@ -51,8 +54,25 @@ jobs: with: sha: ${{ needs.prepare-release.outputs.pre_release_sha }} tag: ${{ inputs.version }} + build-macos: + needs: prepare-release + runs-on: macOS-14 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + - name: Build SwiftLint for macOS + run: make build + - name: Upload binary as artifact + uses: actions/upload-artifact@v4 + with: + name: swiftlint + path: .build/universal/swiftlint + retention-days: 2 create-release: - needs: build-docker + needs: + - build-docker + - build-macos runs-on: macOS-14 steps: - uses: actions/checkout@v4 @@ -63,19 +83,19 @@ jobs: git config --local user.name "Danny Mösch" git config --local user.email "danny.moesch@icloud.com" - name: Create build folders - run: mkdir -p .build/linux - - name: Extract binary from Docker image - uses: shrink/actions-docker-extract@v3 + run: mkdir -p .build/universal .build/linux + - name: Download binary artifact for macOS + uses: actions/download-artifact@v4 with: - image: ghcr.io/realm/swiftlint:${{ inputs.version }} - path: /usr/bin/swiftlint - destination: .build/linux/swiftlint_linux_amd64 - - name: Make binary executable - run: chmod +x .build/linux/swiftlint_linux_amd64 + name: swiftlint + path: .build/universal + - name: Download binary artifact for Linux + uses: actions/download-artifact@v4 + with: + name: swiftlint_linux_amd64 + path: .build/linux - name: Create SPM artifact bundle run: make spm_artifactbundle - env: - DEVELOPER_DIR: /Applications/Xcode_16.2.app - name: Update binary target in Swift package run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" - name: Create tag and release commit @@ -96,19 +116,6 @@ jobs: run: gh release upload ${{ inputs.version }} SwiftLintBinary.artifactbundle.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - upload-docker: - needs: create-release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - - name: Upload binary to existing release - run: | - make zip_linux_release - gh release upload ${{ inputs.version }} swiftlint_linux.zip - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} upload-package: needs: create-release runs-on: macOS-latest diff --git a/Makefile b/Makefile index f3a8eac0fd..29b90a479c 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,7 @@ portable_zip: installables cp -f "$(LICENSE_PATH)" "$(TEMPORARY_FOLDER)" (cd "$(TEMPORARY_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./portable_swiftlint.zip" -spm_artifactbundle: installables installables_linux +spm_artifactbundle: $(SWIFTLINT_EXECUTABLE) $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) mkdir -p "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-macos/bin" mkdir -p "$(ARTIFACT_BUNDLE_PATH)/swiftlint-$(VERSION_STRING)-linux-gnu/bin" sed 's/__VERSION__/$(VERSION_STRING)/g' tools/info.json.template > "$(ARTIFACT_BUNDLE_PATH)/info.json" From 4f056eb32b1bba9fdb2def35517c55b7d29b63e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 13:44:39 +0100 Subject: [PATCH 173/260] Specify multiple outputs --- .github/workflows/docker.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ec17e7e86..f084b674a3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -46,10 +46,15 @@ jobs: registry: ghcr.io - uses: docker/build-push-action@v6 with: - push: true tags: ghcr.io/${{ env.REPOSITORY_LC }}:${{ env.DOCKER_TAG }} platforms: linux/amd64 - outputs: type=local,dest=artifacts + outputs: + - type=registry + - type=local,dest=artifacts + - name: List local files + run: | + ls -l + tree artifacts - name: Upload binary artifact uses: actions/upload-artifact@v4 with: From 62ad0c00f6e581d5ba82d3cb51de94a8a2236e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 13:46:22 +0100 Subject: [PATCH 174/260] Use text block --- .github/workflows/docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f084b674a3..3c547a0d00 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -48,9 +48,9 @@ jobs: with: tags: ghcr.io/${{ env.REPOSITORY_LC }}:${{ env.DOCKER_TAG }} platforms: linux/amd64 - outputs: - - type=registry - - type=local,dest=artifacts + outputs: | + type=registry + type=local,dest=artifacts - name: List local files run: | ls -l From cc08a8794b493af4deea9005afe114dd26b79003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 14:04:13 +0100 Subject: [PATCH 175/260] Take binary from install location --- .github/workflows/docker.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3c547a0d00..06411a6278 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -51,13 +51,9 @@ jobs: outputs: | type=registry type=local,dest=artifacts - - name: List local files - run: | - ls -l - tree artifacts - name: Upload binary artifact uses: actions/upload-artifact@v4 with: name: swiftlint_linux_amd64 - path: artifacts/linux_amd64/workspace/swiftlint + path: artifacts/usr/bin/swiftlint retention-days: 2 From 329f9b020441c5d7c9f9abf15b4155da7181732c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 14:22:27 +0100 Subject: [PATCH 176/260] Throw an error if no files were found --- .github/workflows/docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 06411a6278..44e1158ab2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -56,4 +56,5 @@ jobs: with: name: swiftlint_linux_amd64 path: artifacts/usr/bin/swiftlint + if-no-files-found: error retention-days: 2 From d0a2bfdb5a3b9632c6f5f41101ff7e62e1f4eb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 15:06:03 +0100 Subject: [PATCH 177/260] Ensure that binaries are executable --- .github/workflows/release.yml | 6 +++++- Makefile | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f3d28b768..04aaabe7cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,8 +94,12 @@ jobs: with: name: swiftlint_linux_amd64 path: .build/linux + - name: Make binary executable + run: chmod +x .build/universal/swiftlint .build/linux/swiftlint_linux_amd64 + - name: List downloaded files + run: tree .build - name: Create SPM artifact bundle - run: make spm_artifactbundle + run: make --debug spm_artifactbundle - name: Update binary target in Swift package run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" - name: Create tag and release commit diff --git a/Makefile b/Makefile index 29b90a479c..7f6b280b2b 100644 --- a/Makefile +++ b/Makefile @@ -28,9 +28,9 @@ OUTPUT_PACKAGE=SwiftLint.pkg VERSION_STRING=$(shell ./tools/get-version) -.PHONY: all clean build install package test uninstall docs +.PHONY: all clean install package test uninstall docs -all: build +all: $(SWIFTLINT_EXECUTABLE) sourcery: Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift Source/SwiftLintFramework/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift @@ -72,7 +72,7 @@ clean: clean_xcode: $(BUILD_TOOL) $(XCODEFLAGS) -configuration Test clean -build: +$(SWIFTLINT_EXECUTABLE): mkdir -p "$(SWIFTLINT_EXECUTABLE_PARENT)" bazel build --config release universal_swiftlint $(eval SWIFTLINT_BINARY := $(shell bazel cquery --config release --output=files universal_swiftlint)) @@ -88,7 +88,7 @@ $(SWIFTLINT_EXECUTABLE_LINUX_AMD64): build_with_disable_sandbox: swift build --disable-sandbox $(SWIFT_BUILD_FLAGS) -install: build +install: $(SWIFTLINT_EXECUTABLE) install -d "$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE)" "$(BINARIES_FOLDER)" @@ -96,7 +96,7 @@ uninstall: rm -rf "$(FRAMEWORKS_FOLDER)/SwiftLintFramework.framework" rm -f "$(BINARIES_FOLDER)/swiftlint" -installables: build +installables: $(SWIFTLINT_EXECUTABLE) install -d "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" install "$(SWIFTLINT_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" @@ -134,7 +134,7 @@ zip_linux_release: $(SWIFTLINT_EXECUTABLE_LINUX_AMD64) cp -f "$(LICENSE_PATH)" "$(TMP_FOLDER)" (cd "$(TMP_FOLDER)"; zip -yr - "swiftlint" "LICENSE") > "./swiftlint_linux.zip" -package: build +package: $(SWIFTLINT_EXECUTABLE) $(eval PACKAGE_ROOT := $(shell mktemp -d)) cp "$(SWIFTLINT_EXECUTABLE)" "$(PACKAGE_ROOT)" pkgbuild \ From 5e66728a968893ee32235704916c0d5a19f7f644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 15:18:08 +0100 Subject: [PATCH 178/260] Fix target name --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 04aaabe7cd..8425d003d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: with: ref: ${{ inputs.branch }} - name: Build SwiftLint for macOS - run: make build + run: make .build/universal/swiftlint - name: Upload binary as artifact uses: actions/upload-artifact@v4 with: From 265a2ff621a61a81a9194577bddd9bc886d575d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 15:18:56 +0100 Subject: [PATCH 179/260] Fail if no binary was built --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8425d003d8..60ef75bba3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,6 +69,7 @@ jobs: name: swiftlint path: .build/universal/swiftlint retention-days: 2 + if-no-files-found: error create-release: needs: - build-docker From d1fefcf1dabe12ae4956d775ed10e9a06f4cb8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 15:37:49 +0100 Subject: [PATCH 180/260] Always list files --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60ef75bba3..ff8fe9f89e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -98,7 +98,8 @@ jobs: - name: Make binary executable run: chmod +x .build/universal/swiftlint .build/linux/swiftlint_linux_amd64 - name: List downloaded files - run: tree .build + if: always() + run: tree .build/{universal,linux} - name: Create SPM artifact bundle run: make --debug spm_artifactbundle - name: Update binary target in Swift package From 3ed52145f0c9bd043e6a7f147f1826458a6c2b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 15:51:16 +0100 Subject: [PATCH 181/260] Don't use `tree` --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff8fe9f89e..6ae3cec080 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,7 +99,10 @@ jobs: run: chmod +x .build/universal/swiftlint .build/linux/swiftlint_linux_amd64 - name: List downloaded files if: always() - run: tree .build/{universal,linux} + run: | + ls -al .build + ls -al .build/universal + ls -al .build/linux - name: Create SPM artifact bundle run: make --debug spm_artifactbundle - name: Update binary target in Swift package From bae1a6ac39f9955557cb5a27c9eca751319bad9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 16:09:39 +0100 Subject: [PATCH 182/260] Rename Linux binary before upload --- .github/workflows/docker.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 44e1158ab2..b7a62706f6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -51,10 +51,12 @@ jobs: outputs: | type=registry type=local,dest=artifacts + - name: Rename binary artifact + run: mv artifacts/usr/bin/swiftlint artifacts/usr/bin/swiftlint_linux_amd64 - name: Upload binary artifact uses: actions/upload-artifact@v4 with: name: swiftlint_linux_amd64 - path: artifacts/usr/bin/swiftlint + path: artifacts/usr/bin/swiftlint_linux_amd64 if-no-files-found: error retention-days: 2 From b6d14db9dffeb03f9cf2eb36475d3ca880c64b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 16:46:40 +0100 Subject: [PATCH 183/260] Transfer Bazel release between jobs --- .github/workflows/release.yml | 25 ++++++++++++++++--------- Makefile | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ae3cec080..0c714b193c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,8 @@ on: env: DEVELOPER_DIR: /Applications/Xcode_16.2.app + MACOS_BUILD_DIR: .build/universal + LINUX_BUILD_DIR: .build/linux jobs: prepare-release: @@ -62,12 +64,15 @@ jobs: with: ref: ${{ inputs.branch }} - name: Build SwiftLint for macOS - run: make .build/universal/swiftlint + run: make bazel_release - name: Upload binary as artifact uses: actions/upload-artifact@v4 with: name: swiftlint - path: .build/universal/swiftlint + path: | + ${{ env.MACOS_BUILD_DIR }}/swiftlint + bazel-bin/bazel.tar.gz + bazel-bin/bazel.tar.gz.sha256 retention-days: 2 if-no-files-found: error create-release: @@ -84,25 +89,27 @@ jobs: git config --local user.name "Danny Mösch" git config --local user.email "danny.moesch@icloud.com" - name: Create build folders - run: mkdir -p .build/universal .build/linux + run: mkdir -p ${{ env.MACOS_BUILD_DIR }} ${{ env.LINUX_BUILD_DIR }} - name: Download binary artifact for macOS uses: actions/download-artifact@v4 with: name: swiftlint - path: .build/universal + path: ${{ env.MACOS_BUILD_DIR }} + - name: Move Bazel release + run: mv -f ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz.sha256 . - name: Download binary artifact for Linux uses: actions/download-artifact@v4 with: name: swiftlint_linux_amd64 - path: .build/linux - - name: Make binary executable - run: chmod +x .build/universal/swiftlint .build/linux/swiftlint_linux_amd64 + path: ${{ env.LINUX_BUILD_DIR }} + - name: Make binaries executable + run: chmod +x ${{ env.MACOS_BUILD_DIR }}/swiftlint ${{ env.LINUX_BUILD_DIR }}/swiftlint_linux_amd64 - name: List downloaded files if: always() run: | ls -al .build - ls -al .build/universal - ls -al .build/linux + ls -al ${{ env.MACOS_BUILD_DIR }} + ls -al ${{ env.LINUX_BUILD_DIR }} - name: Create SPM artifact bundle run: make --debug spm_artifactbundle - name: Update binary target in Swift package diff --git a/Makefile b/Makefile index 7f6b280b2b..99d1378a9a 100644 --- a/Makefile +++ b/Makefile @@ -147,9 +147,9 @@ package: $(SWIFTLINT_EXECUTABLE) bazel_test: bazel test --test_output=errors //Tests/... -bazel_release: +bazel_release: $(SWIFTLINT_EXECUTABLE) bazel build :release - mv bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 . + mv -f bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 . docker_image: docker build --platform linux/amd64 --force-rm --tag swiftlint . From 6d0f41691326541f828562685af02acb1b4a9b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 17:30:42 +0100 Subject: [PATCH 184/260] Move files to the same level before upload --- .github/workflows/release.yml | 18 +++++++++--------- Makefile | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c714b193c..148a7e39d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,14 +65,14 @@ jobs: ref: ${{ inputs.branch }} - name: Build SwiftLint for macOS run: make bazel_release - - name: Upload binary as artifact + - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: swiftlint path: | - ${{ env.MACOS_BUILD_DIR }}/swiftlint - bazel-bin/bazel.tar.gz - bazel-bin/bazel.tar.gz.sha256 + swiftlint + bazel.tar.gz + bazel.tar.gz.sha256 retention-days: 2 if-no-files-found: error create-release: @@ -95,21 +95,21 @@ jobs: with: name: swiftlint path: ${{ env.MACOS_BUILD_DIR }} - - name: Move Bazel release - run: mv -f ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz.sha256 . - name: Download binary artifact for Linux uses: actions/download-artifact@v4 with: name: swiftlint_linux_amd64 path: ${{ env.LINUX_BUILD_DIR }} - - name: Make binaries executable - run: chmod +x ${{ env.MACOS_BUILD_DIR }}/swiftlint ${{ env.LINUX_BUILD_DIR }}/swiftlint_linux_amd64 - name: List downloaded files if: always() run: | ls -al .build - ls -al ${{ env.MACOS_BUILD_DIR }} ls -al ${{ env.LINUX_BUILD_DIR }} + ls -al ${{ env.MACOS_BUILD_DIR }} + - name: Move Bazel release + run: mv -f ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz.sha256 . + - name: Make binaries executable + run: chmod +x ${{ env.MACOS_BUILD_DIR }}/swiftlint ${{ env.LINUX_BUILD_DIR }}/swiftlint_linux_amd64 - name: Create SPM artifact bundle run: make --debug spm_artifactbundle - name: Update binary target in Swift package diff --git a/Makefile b/Makefile index 99d1378a9a..9fa3fbbaf2 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ bazel_test: bazel_release: $(SWIFTLINT_EXECUTABLE) bazel build :release - mv -f bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 . + mv -f bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 $(SWIFTLINT_EXECUTABLE) . docker_image: docker build --platform linux/amd64 --force-rm --tag swiftlint . From aa2a0973d447de837e3baaf8958feab7a0222a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 20:43:25 +0100 Subject: [PATCH 185/260] Split post-release actions from release workflow --- .github/workflows/docker.yml | 1 + .github/workflows/post-release.yml | 45 ++++++++++++++++++++++ .github/workflows/release.yml | 60 ------------------------------ 3 files changed, 46 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/post-release.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b7a62706f6..bac09c9ea9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -54,6 +54,7 @@ jobs: - name: Rename binary artifact run: mv artifacts/usr/bin/swiftlint artifacts/usr/bin/swiftlint_linux_amd64 - name: Upload binary artifact + if: github.event_name != 'push' uses: actions/upload-artifact@v4 with: name: swiftlint_linux_amd64 diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 0000000000..e8542cc5e5 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,45 @@ +name: Post Release + +on: + release: + types: + - published + +jobs: + publish-pod: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + - name: Retrieve author in uppercase + id: retrieve_author + run: | + AUTHOR=$(echo ${{ github.event.release.author }} | tr '[:lower:]' '[:upper:]') + echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + - name: Deploy to CocoaPods + run: make pod_publish + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} + dispatch-plugins: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + - name: Parse checksum + id: parse_checksum + run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT + - name: Dispatch release of plugins package + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.SIMPLYDANNY_PLUGINS_SYNC }} + repository: SimplyDanny/SwiftLintPlugins + event-type: swiftlint-release + client-payload: |- + { + "title": "${{ github.event.release.name }}", + "tag": "${{ github.event.release.tag_name }}", + "checksum": "${{ steps.parse_checksum.outputs.checksum }}" + } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 148a7e39d8..f9113a9327 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -132,81 +132,21 @@ jobs: run: gh release upload ${{ inputs.version }} SwiftLintBinary.artifactbundle.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - upload-package: - needs: create-release - runs-on: macOS-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - name: Create package run: make package - name: Upload package to existing release run: gh release upload ${{ inputs.version }} SwiftLint.pkg env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - upload-bazel: - needs: create-release - runs-on: macOS-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - name: Create Bazel release run: make bazel_release - name: Upload Bazel release to existing release run: gh release upload ${{ inputs.version }} bazel.tar.gz bazel.tar.gz.sha256 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - upload-portable-zip: - needs: create-release - runs-on: macOS-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - name: Create portable ZIP run: make portable_zip - name: Upload portable ZIP to existing release run: gh release upload ${{ inputs.version }} portable_swiftlint.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - publish-pod: - needs: create-release - runs-on: macOS-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - - name: Retrieve author in uppercase - id: retrieve_author - run: | - AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') - echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - - name: Deploy to CocoaPods - run: make pod_publish - env: - COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} - dispatch-plugins: - needs: create-release - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - - name: Parse checksum - id: parse_checksum - run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT - - name: Dispatch release of plugins package - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SIMPLYDANNY_PLUGINS_SYNC }} - repository: SimplyDanny/SwiftLintPlugins - event-type: swiftlint-release - client-payload: |- - { - "title": "${{ inputs.title }}", - "tag": "${{ inputs.version }}", - "checksum": "${{ steps.parse_checksum.outputs.checksum }}" - } From 470d27d443015fbbf769a58108d8dfce7d41d7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 21:10:13 +0100 Subject: [PATCH 186/260] Use existing functions --- .github/workflows/docker.yml | 6 +-- .github/workflows/post-release.yml | 14 +++---- .github/workflows/release.yml | 59 ++++++++---------------------- 3 files changed, 26 insertions(+), 53 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index bac09c9ea9..3aeae54ec9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,8 +6,8 @@ on: - main workflow_call: inputs: - sha: - description: 'Git commit SHA' + ref: + description: 'Git reference' required: true type: string tag: @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 if: github.event_name != 'push' && inputs.tag != 'latest' with: - ref: ${{ inputs.sha }} + ref: ${{ inputs.ref }} - uses: actions/checkout@v4 if: github.event_name == 'push' - name: Set Docker tag diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index e8542cc5e5..7842794198 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -2,12 +2,11 @@ name: Post Release on: release: - types: - - published + types: published jobs: publish-pod: - runs-on: macOS-latest + runs-on: macOS-14 steps: - uses: actions/checkout@v4 with: @@ -20,9 +19,10 @@ jobs: - name: Deploy to CocoaPods run: make pod_publish env: + DEVELOPER_DIR: /Applications/Xcode_16.2.app COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} dispatch-plugins: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: event-type: swiftlint-release client-payload: |- { - "title": "${{ github.event.release.name }}", - "tag": "${{ github.event.release.tag_name }}", - "checksum": "${{ steps.parse_checksum.outputs.checksum }}" + "title": "${{ github.event.release.name }}", + "tag": "${{ github.event.release.tag_name }}", + "checksum": "${{ steps.parse_checksum.outputs.checksum }}" } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9113a9327..4bd3a0471a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,9 +24,7 @@ env: jobs: prepare-release: - runs-on: ubuntu-latest - outputs: - pre_release_sha: ${{ steps.pre_release.outputs.pre_release_sha }} + runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -47,14 +45,13 @@ jobs: id: pre_release run: | git commit -a -m "Prepare ${{ inputs.version }} release" - echo "pre_release_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT git push origin HEAD build-docker: needs: prepare-release uses: ./.github/workflows/docker.yml secrets: inherit with: - sha: ${{ needs.prepare-release.outputs.pre_release_sha }} + ref: ${{ inputs.branch }} tag: ${{ inputs.version }} build-macos: needs: prepare-release @@ -64,7 +61,7 @@ jobs: with: ref: ${{ inputs.branch }} - name: Build SwiftLint for macOS - run: make bazel_release + run: make --debug bazel_release - name: Upload build artifacts uses: actions/upload-artifact@v4 with: @@ -100,53 +97,29 @@ jobs: with: name: swiftlint_linux_amd64 path: ${{ env.LINUX_BUILD_DIR }} - - name: List downloaded files - if: always() - run: | - ls -al .build - ls -al ${{ env.LINUX_BUILD_DIR }} - ls -al ${{ env.MACOS_BUILD_DIR }} - name: Move Bazel release run: mv -f ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz ${{ env.MACOS_BUILD_DIR }}/bazel.tar.gz.sha256 . - name: Make binaries executable run: chmod +x ${{ env.MACOS_BUILD_DIR }}/swiftlint ${{ env.LINUX_BUILD_DIR }}/swiftlint_linux_amd64 - - name: Create SPM artifact bundle - run: make --debug spm_artifactbundle + - name: Create artifacts + run: | + make --debug spm_artifactbundle + make --debug package + make --debug portable_zip - name: Update binary target in Swift package run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" - name: Create tag and release commit run: | git commit -a -m "Release ${{ inputs.version }}" - git tag -a ${{ inputs.version }} -m "${{ inputs.title }}" + git tag -a "${{ inputs.version }}"" -m "${{ inputs.title }}" git push origin HEAD - git push origin ${{ inputs.version }} + git push origin "${{ inputs.version }}" - name: Create release - run: | - release_notes=$(mktemp) - ./tools/generate-release-notes.sh "${{ inputs.version }}" > "$release_notes" - gh release create ${{ inputs.version }} --title ${{ inputs.title }} -F "$release_notes" --draft - rm "$release_notes" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload artifact bundle to release - run: gh release upload ${{ inputs.version }} SwiftLintBinary.artifactbundle.zip - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create package - run: make package - - name: Upload package to existing release - run: gh release upload ${{ inputs.version }} SwiftLint.pkg - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Bazel release - run: make bazel_release - - name: Upload Bazel release to existing release - run: gh release upload ${{ inputs.version }} bazel.tar.gz bazel.tar.gz.sha256 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create portable ZIP - run: make portable_zip - - name: Upload portable ZIP to existing release - run: gh release upload ${{ inputs.version }} portable_swiftlint.zip + run: ./tools/create-github-release.sh "${{ inputs.version }}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Add new changelog section + run: | + ./tools/add-new-changelog-section.sh + git commit -a -m "Add new changelog section" + git push origin HEAD From ca80b0e4adac7032f97e73b00b42680546f02b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 21:29:46 +0100 Subject: [PATCH 187/260] Add human-friendly names to build jobs --- .github/workflows/docker.yml | 1 + .github/workflows/lint.yml | 1 + .github/workflows/plugins-sync.yml | 1 + .github/workflows/post-release.yml | 2 ++ .github/workflows/release.yml | 6 +++++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3aeae54ec9..dfb18b4098 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,6 +18,7 @@ on: jobs: build: + name: Build Docker Image runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 384ce840c2..ce9d9f7c34 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,7 @@ on: jobs: lint: + name: Lint Repository runs-on: ubuntu-24.04 # "Noble Numbat" container: swift:6.0-noble steps: diff --git a/.github/workflows/plugins-sync.yml b/.github/workflows/plugins-sync.yml index e517bd3158..0fb2a50ef8 100644 --- a/.github/workflows/plugins-sync.yml +++ b/.github/workflows/plugins-sync.yml @@ -10,6 +10,7 @@ on: jobs: sync: + name: Sync Plugins Folder runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 7842794198..a58f98a074 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -6,6 +6,7 @@ on: jobs: publish-pod: + name: Publish Pod runs-on: macOS-14 steps: - uses: actions/checkout@v4 @@ -22,6 +23,7 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_16.2.app COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} dispatch-plugins: + name: Dispatch Plugins Repository runs-on: ubuntu-24.04 steps: - name: Checkout repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bd3a0471a..5b64a16cee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ env: jobs: prepare-release: + name: Prepare Release runs-on: ubuntu-24.04 steps: - name: Checkout repository @@ -47,6 +48,7 @@ jobs: git commit -a -m "Prepare ${{ inputs.version }} release" git push origin HEAD build-docker: + name: Build Linux Binaries needs: prepare-release uses: ./.github/workflows/docker.yml secrets: inherit @@ -54,6 +56,7 @@ jobs: ref: ${{ inputs.branch }} tag: ${{ inputs.version }} build-macos: + name: Build macOS Binaries needs: prepare-release runs-on: macOS-14 steps: @@ -73,6 +76,7 @@ jobs: retention-days: 2 if-no-files-found: error create-release: + name: Create Release needs: - build-docker - build-macos @@ -111,7 +115,7 @@ jobs: - name: Create tag and release commit run: | git commit -a -m "Release ${{ inputs.version }}" - git tag -a "${{ inputs.version }}"" -m "${{ inputs.title }}" + git tag -a "${{ inputs.version }}" -m "${{ inputs.title }}" git push origin HEAD git push origin "${{ inputs.version }}" - name: Create release From fd42f7867fd4a5ceaa7bd1a8c4ee117b70e63c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 21:53:33 +0100 Subject: [PATCH 188/260] Add zipped Linux binary to release --- .github/workflows/release.yml | 1 + tools/create-github-release.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b64a16cee..97fbacd13d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -110,6 +110,7 @@ jobs: make --debug spm_artifactbundle make --debug package make --debug portable_zip + make --debug zip_linux_release - name: Update binary target in Swift package run: ./tools/update-artifact-bundle.sh "${{ inputs.version }}" - name: Create tag and release commit diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 8256ac5a6c..748296ac31 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -16,6 +16,7 @@ gh release create "$version" --title "$release_title" -F "$release_notes" --draf "bazel.tar.gz" \ "bazel.tar.gz.sha256" \ "portable_swiftlint.zip" \ + "swiftlint_linux.zip" \ "SwiftLint.pkg" \ "SwiftLintBinary.artifactbundle.zip" From cd2c40ebb525e82dffe213e344dfe06c2f88ec1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 22:51:20 +0100 Subject: [PATCH 189/260] Use GitHub Actions Bot as committer --- .github/workflows/post-release.yml | 2 +- .github/workflows/release.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index a58f98a074..5a155f496d 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -15,7 +15,7 @@ jobs: - name: Retrieve author in uppercase id: retrieve_author run: | - AUTHOR=$(echo ${{ github.event.release.author }} | tr '[:lower:]' '[:upper:]') + AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Deploy to CocoaPods run: make pod_publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97fbacd13d..48a3d5d957 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,8 +40,8 @@ jobs: sed -i -e '3s/.*/ version = "${{ inputs.version }}",/' MODULE.bazel - name: Configure author run: | - git config --local user.name "Danny Mösch" - git config --local user.email "danny.moesch@icloud.com" + git config --local user.name "github-actions[bot]" + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Commit changes id: pre_release run: | @@ -87,8 +87,8 @@ jobs: ref: ${{ inputs.branch }} - name: Configure author run: | - git config --local user.name "Danny Mösch" - git config --local user.email "danny.moesch@icloud.com" + git config --local user.name "github-actions[bot]" + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Create build folders run: mkdir -p ${{ env.MACOS_BUILD_DIR }} ${{ env.LINUX_BUILD_DIR }} - name: Download binary artifact for macOS From 7b80780ab030777c4e2866c868f5b2ae3c56622c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 23:26:06 +0100 Subject: [PATCH 190/260] Abort in case release tag doesn't yet exist --- tools/create-github-release.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 748296ac31..3671e42e4e 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -12,12 +12,12 @@ release_notes=$(mktemp) # Create GitHub Release release_title="$(sed -n '1s/^## //p' CHANGELOG.md)" -gh release create "$version" --title "$release_title" -F "$release_notes" --draft \ - "bazel.tar.gz" \ - "bazel.tar.gz.sha256" \ - "portable_swiftlint.zip" \ - "swiftlint_linux.zip" \ - "SwiftLint.pkg" \ +gh release create "$version" --title "$release_title" -F "$release_notes" --draft --verify-tag \ + "bazel.tar.gz" \ + "bazel.tar.gz.sha256" \ + "portable_swiftlint.zip" \ + "swiftlint_linux.zip" \ + "SwiftLint.pkg" \ "SwiftLintBinary.artifactbundle.zip" rm "$release_notes" From 526371222b4a922c91253af873194a439aa4bf84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 11 Jan 2025 23:40:54 +0100 Subject: [PATCH 191/260] Revert "Prepare for 0.58.0 release" This reverts commit ffa1979c880c77bd4c58a2355f8ef7286e445af8. --- Package.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 26754933cb..8e3fd077f6 100644 --- a/Package.swift +++ b/Package.swift @@ -194,10 +194,11 @@ let package = Package( ) #if os(macOS) +// TODO: in the next release the artifactbundle is not suffixed with "-macos" package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary.artifactbundle.zip", + url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary-macos.artifactbundle.zip", checksum: "c88bf3e5bc1326d8ca66bc3f9eae786f2094c5172cd70b26b5f07686bb883899" ) ) From 14e9a334c20a9e87cac325f1cf97ea9167cafc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 12:31:32 +0100 Subject: [PATCH 192/260] Mention lowest compiler version that builds SwiftLint successfully --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8430f28289..5546cff42c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ * The `inert_defer` and `unused_capture_list` rules have completely been removed after being deprecated for 2 years. [SimplyDanny](https://github.com/SimplyDanny) -* SwiftLint now requires a Swift 6 or higher compiler to build. +* SwiftLint now requires a Swift 5.10 or higher compiler to build. [The Swift Package Manager plugins](https://github.com/SimplyDanny/SwiftLintPlugins) continue to work with Swift 5.9. [SimplyDanny](https://github.com/SimplyDanny) * The `private_unit_test` rule's deprecated `regex` configuration option has been removed after 2 years. From e9b976e3e352658a54443a7854cdd39cc590de1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 12:35:32 +0100 Subject: [PATCH 193/260] Checkout or create branch --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48a3d5d957..9575b23f7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,8 +30,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create release branch - if: inputs.branch != 'main' - run: git checkout -b ${{ inputs.branch }} + run: git checkout ${{ inputs.branch }} || git checkout -b ${{ inputs.branch }} - name: Update changelog run: "sed -i 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions From 2ae7406a99ebada256e33e55d235e3bbc0caf9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 13:20:28 +0100 Subject: [PATCH 194/260] Derive release branch name --- .github/workflows/release.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9575b23f7e..11e5646c06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,16 +11,12 @@ on: description: 'Release title' required: true type: string - branch: - description: 'Release branch' - required: false - type: string - default: 'main' env: DEVELOPER_DIR: /Applications/Xcode_16.2.app MACOS_BUILD_DIR: .build/universal LINUX_BUILD_DIR: .build/linux + RELEASE_BRANCH: release/${{ inputs.version }} jobs: prepare-release: @@ -30,7 +26,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create release branch - run: git checkout ${{ inputs.branch }} || git checkout -b ${{ inputs.branch }} + run: git checkout ${{ env.RELEASE_BRANCH }} || git checkout -b ${{ env.RELEASE_BRANCH }} - name: Update changelog run: "sed -i 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions @@ -52,7 +48,7 @@ jobs: uses: ./.github/workflows/docker.yml secrets: inherit with: - ref: ${{ inputs.branch }} + ref: release/${{ inputs.version }} tag: ${{ inputs.version }} build-macos: name: Build macOS Binaries @@ -61,7 +57,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.branch }} + ref: ${{ env.RELEASE_BRANCH }} - name: Build SwiftLint for macOS run: make --debug bazel_release - name: Upload build artifacts @@ -83,7 +79,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.branch }} + ref: ${{ env.RELEASE_BRANCH }} - name: Configure author run: | git config --local user.name "github-actions[bot]" From 1f060dd8ce7ff95c5193bfc21368dd3f36271209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 9 Jan 2025 20:43:16 +0100 Subject: [PATCH 195/260] Prepare for 0.58.0 release --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 8e3fd077f6..26754933cb 100644 --- a/Package.swift +++ b/Package.swift @@ -194,11 +194,10 @@ let package = Package( ) #if os(macOS) -// TODO: in the next release the artifactbundle is not suffixed with "-macos" package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary-macos.artifactbundle.zip", + url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary.artifactbundle.zip", checksum: "c88bf3e5bc1326d8ca66bc3f9eae786f2094c5172cd70b26b5f07686bb883899" ) ) From a86ab8d5e1b1cd5c282ee405afdd66779677366c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 13:06:10 +0100 Subject: [PATCH 196/260] Name release assets --- tools/create-github-release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 3671e42e4e..99ddeb9269 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -15,9 +15,9 @@ release_title="$(sed -n '1s/^## //p' CHANGELOG.md)" gh release create "$version" --title "$release_title" -F "$release_notes" --draft --verify-tag \ "bazel.tar.gz" \ "bazel.tar.gz.sha256" \ - "portable_swiftlint.zip" \ - "swiftlint_linux.zip" \ - "SwiftLint.pkg" \ + "portable_swiftlint.zip#Universal macOS Binary" \ + "swiftlint_linux.zip#AMD64 Linux Binary" \ + "SwiftLint.pkg#Universal macOS Installer" \ "SwiftLintBinary.artifactbundle.zip" rm "$release_notes" From 0c7d00983937f3abdc944b78335f362890d7c29d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:23:29 +0000 Subject: [PATCH 197/260] Prepare 0.58.0 release --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Source/SwiftLintFramework/Models/Version.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5546cff42c..3c72284228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Main +## 0.58.0: New Year’s Fresh Fold #### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index a016dbbe0e..9b834ef077 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.57.1", + version = "0.58.0", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Source/SwiftLintFramework/Models/Version.swift b/Source/SwiftLintFramework/Models/Version.swift index 8b29116a70..d381b75090 100644 --- a/Source/SwiftLintFramework/Models/Version.swift +++ b/Source/SwiftLintFramework/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable, Sendable { } /// The current SwiftLint version. - public static let current = Self(value: "0.57.1") + public static let current = Self(value: "0.58.0") /// Public initializer. /// From fcd4374431159a645d27d549ac3879f3e0ee1fa6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:35:01 +0000 Subject: [PATCH 198/260] Release 0.58.0 --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 26754933cb..f698365767 100644 --- a/Package.swift +++ b/Package.swift @@ -197,8 +197,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.57.1/SwiftLintBinary.artifactbundle.zip", - checksum: "c88bf3e5bc1326d8ca66bc3f9eae786f2094c5172cd70b26b5f07686bb883899" + url: "https://github.com/realm/SwiftLint/releases/download/0.58.0/SwiftLintBinary.artifactbundle.zip", + checksum: "a9a9b4f94c4e0c65eda72c03c7e10d8105b0a50cbde18d375dab9b6e4fd9c052" ) ) #endif From f5299a481b565258dda32e25c0c6afb5e9dd5fc1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:35:08 +0000 Subject: [PATCH 199/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c72284228..084a6f952e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## Main + +#### Breaking + +* None. + +#### Experimental + +* None. + +#### Enhancements + +* None. + +#### Bug Fixes + +* None. + ## 0.58.0: New Year’s Fresh Fold #### Breaking From 22eeaf4c5c7545aa867e17196fdb5ed41a3ec32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 14:05:31 +0100 Subject: [PATCH 200/260] Configure patch strip in Bazel template --- .bcr/source.template.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.bcr/source.template.json b/.bcr/source.template.json index cbb800e880..5060b8f0f7 100644 --- a/.bcr/source.template.json +++ b/.bcr/source.template.json @@ -1,5 +1,6 @@ { "url": "https://github.com/realm/SwiftLint/releases/download/{TAG}/bazel.tar.gz", "integrity": "", - "strip_prefix": "" + "strip_prefix": "", + "patch_strip": 0 } From 06e8461bb3508d81a9506360c1b584a222f3b6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Jan 2025 14:06:47 +0100 Subject: [PATCH 201/260] Update Gemfile.lock --- Gemfile.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c67e893bb2..dd8ed0f55a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,6 +108,7 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liferaft (0.0.6) + mini_portile2 (2.8.8) minitest (5.19.0) molinillo (0.8.0) mustache (1.1.1) @@ -131,8 +132,8 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - sqlite3 (1.7.3-arm64-darwin) - sqlite3 (1.7.3-x86_64-linux) + sqlite3 (1.7.3) + mini_portile2 (~> 2.8.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) typhoeus (1.4.0) From f045130e3e8e5bcaf09cc336b7b947d4bdc4b7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 09:43:17 +0100 Subject: [PATCH 202/260] Remove unused protocol `CollectingCorrectableRule` (#5951) --- .../Protocols/CollectingRule.swift | 63 ------------------- .../FrameworkTests/CollectingRuleTests.swift | 15 ++++- 2 files changed, 12 insertions(+), 66 deletions(-) diff --git a/Source/SwiftLintCore/Protocols/CollectingRule.swift b/Source/SwiftLintCore/Protocols/CollectingRule.swift index 3bacde8e81..eb7bff0541 100644 --- a/Source/SwiftLintCore/Protocols/CollectingRule.swift +++ b/Source/SwiftLintCore/Protocols/CollectingRule.swift @@ -89,69 +89,6 @@ public extension CollectingRule where Self: AnalyzerRule { } } -/// A `CollectingRule` that is also a `CorrectableRule`. -package protocol CollectingCorrectableRule: CollectingRule, CorrectableRule { - /// Attempts to correct the violations to this rule in the specified file after collecting file info for all files - /// and returns all corrections that were applied. - /// - /// - note: This function is called by the linter and is always implemented in extensions. - /// - /// - parameter file: The file for which to execute the rule. - /// - parameter collectedInfo: All collected info. - /// - parameter compilerArguments: The compiler arguments needed to compile this file. - /// - /// - returns: All corrections that were applied. - func correct(file: SwiftLintFile, - collectedInfo: [SwiftLintFile: FileInfo], - compilerArguments: [String]) -> [Correction] - - /// Attempts to correct the violations to this rule in the specified file after collecting file info for all files - /// and returns all corrections that were applied. - /// - /// - note: This function is called by the linter and is always implemented in extensions. - /// - /// - parameter file: The file for which to execute the rule. - /// - parameter collectedInfo: All collected info. - /// - /// - returns: All corrections that were applied. - func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [Correction] -} - -package extension CollectingCorrectableRule { - func correct(file: SwiftLintFile, - collectedInfo: [SwiftLintFile: FileInfo], - compilerArguments _: [String]) -> [Correction] { - correct(file: file, collectedInfo: collectedInfo) - } - - func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [Correction] { - guard let info = storage.collectedInfo(for: self) else { - queuedFatalError("Attempt to correct a CollectingRule before collecting info for it") - } - return correct(file: file, collectedInfo: info, compilerArguments: compilerArguments) - } - - func correct(file _: SwiftLintFile) -> [Correction] { - queuedFatalError("Must call `correct(file:collectedInfo:)` for AnalyzerRule") - } - - func correct(file _: SwiftLintFile, compilerArguments _: [String]) -> [Correction] { - queuedFatalError("Must call `correct(file:collectedInfo:compilerArguments:)` for AnalyzerRule") - } -} - -package extension CollectingCorrectableRule where Self: AnalyzerRule { - func correct(file _: SwiftLintFile) -> [Correction] { - queuedFatalError("Must call `correct(file:collectedInfo:compilerArguments:)` for AnalyzerRule") - } - func correct(file _: SwiftLintFile, compilerArguments _: [String]) -> [Correction] { - queuedFatalError("Must call `correct(file:collectedInfo:compilerArguments:)` for AnalyzerRule") - } - func correct(file _: SwiftLintFile, collectedInfo _: [SwiftLintFile: FileInfo]) -> [Correction] { - queuedFatalError("Must call `correct(file:collectedInfo:compilerArguments:)` for AnalyzerRule") - } -} - // MARK: - == Implementations /// :nodoc: diff --git a/Tests/FrameworkTests/CollectingRuleTests.swift b/Tests/FrameworkTests/CollectingRuleTests.swift index 3ff16a1dd6..bc160a2670 100644 --- a/Tests/FrameworkTests/CollectingRuleTests.swift +++ b/Tests/FrameworkTests/CollectingRuleTests.swift @@ -71,8 +71,9 @@ final class CollectingRuleTests: SwiftLintTestCase { XCTAssertFalse(violations(Example("_ = 0"), config: Spec.configuration!, requiresFileOnDisk: true).isEmpty) } + // swiftlint:disable:next function_body_length func testCorrects() { - struct Spec: MockCollectingRule, CollectingCorrectableRule { + struct Spec: MockCollectingRule, CorrectableRule { var configuration = SeverityConfiguration(.warning) func collectInfo(for file: SwiftLintFile) -> String { @@ -102,12 +103,16 @@ final class CollectingRuleTests: SwiftLintTestCase { } return [] } + + func correct(file: SwiftLintFile) -> [Correction] { + correct(file: file, collectedInfo: [file: collectInfo(for: file)]) + } } - struct AnalyzerSpec: MockCollectingRule, AnalyzerRule, CollectingCorrectableRule { + struct AnalyzerSpec: MockCollectingRule, AnalyzerRule, CorrectableRule { var configuration = SeverityConfiguration(.warning) - func collectInfo(for file: SwiftLintFile, compilerArguments _: [String]) -> String { + func collectInfo(for file: SwiftLintFile) -> String { file.contents } @@ -131,6 +136,10 @@ final class CollectingRuleTests: SwiftLintTestCase { ? [Correction(ruleDescription: Spec.description, location: Location(file: file, byteOffset: 2))] : [] } + + func correct(file: SwiftLintFile) -> [Correction] { + correct(file: file, collectedInfo: [file: collectInfo(for: file)], compilerArguments: []) + } } let inputs = ["foo", "baz"] From 3f2b86c54d981122ee4bb20d900bbad96a2f5395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 20:18:54 +0100 Subject: [PATCH 203/260] Remove trailing comma when `Sendable` was last (#5955) --- CHANGELOG.md | 4 ++- .../Rules/Lint/RedundantSendableRule.swift | 31 +++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 084a6f952e..94c566c5c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ #### Bug Fixes -* None. +* Fix `redundant_sendable` correction by removing a remaining trailing comma as well when `Sendable` was last. + [SimplyDanny](https://github.com/SimplyDanny) + [#5952](https://github.com/realm/SwiftLint/issues/5952) ## 0.58.0: New Year’s Fresh Fold diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift index d29cdc54eb..15655bc787 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift @@ -24,12 +24,20 @@ struct RedundantSendableRule: Rule { corrections: [ Example("@MainActor struct S: Sendable {}"): Example("@MainActor struct S {}"), - Example("actor A: Sendable {}"): - Example("actor A {}"), + Example("actor A: Sendable /* trailing comment */{}"): + Example("actor A /* trailing comment */{}"), Example("@MyActor enum E: Sendable { case a }", configuration: ["global_actors": ["MyActor"]]): Example("@MyActor enum E { case a }"), - Example("actor A: B, Sendable, C {}"): - Example("actor A: B, C {}"), + Example(""" + actor A: B, Sendable, C // comment + {} + """): + Example(""" + actor A: B, C // comment + {} + """), + Example("@MainActor protocol P: A, Sendable {}"): + Example("@MainActor protocol P: A {}"), ] ) } @@ -114,12 +122,12 @@ private extension DeclGroupSyntax where Self: NamedDeclSyntax { return self } let inheritedTypes = inheritanceClause.inheritedTypes.filter { !$0.isSendable } - if inheritedTypes.isEmpty { - return with(\.inheritanceClause, nil) - .with(\.name.trailingTrivia, inheritanceClause.leadingTrivia + inheritanceClause.trailingTrivia) + if let lastType = inheritedTypes.last, let lastIndex = inheritedTypes.index(of: lastType) { + return with(\.inheritanceClause, inheritanceClause + .with(\.inheritedTypes, inheritedTypes.with(\.[lastIndex], lastType.withoutComma))) } - return with(\.inheritanceClause, inheritanceClause - .with(\.inheritedTypes, inheritedTypes)) + return with(\.inheritanceClause, nil) + .with(\.name.trailingTrivia, inheritanceClause.leadingTrivia + inheritanceClause.trailingTrivia) } } @@ -127,4 +135,9 @@ private extension InheritedTypeSyntax { var isSendable: Bool { type.as(IdentifierTypeSyntax.self)?.name.text == "Sendable" } + + var withoutComma: InheritedTypeSyntax { + with(\.trailingComma, nil) + .with(\.trailingTrivia, trailingTrivia) + } } From bd73b125cc8f2639637dbfc7cf528101811563f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 20:23:38 +0100 Subject: [PATCH 204/260] Fix path to macOS binary in artifact bundle (#5957) --- CHANGELOG.md | 17 ++++++++++++++++- README.md | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94c566c5c8..7fa4b6e606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,22 @@ #### Breaking -* None. +* If you are referring to the `swiftlint` binary from an Artifact Bundle consumed via Swift Package Manager + in an Xcode Run Script Build Phase, make sure to update the path from + + ```bash + "$SWIFT_PACKAGE_DIR"/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-*/bin/swiftlint + ``` + + to + + ```bash + "$SWIFT_PACKAGE_DIR"/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-*-macos/bin/swiftlint + ``` + + in order to make Xcode use the binary built for macOS. + [SimplyDanny](https://github.com/SimplyDanny) + [#5954](https://github.com/realm/SwiftLint/issues/5954) #### Experimental diff --git a/README.md b/README.md index ec07285e0c..c2b70e245f 100644 --- a/README.md +++ b/README.md @@ -339,7 +339,7 @@ following way: ```bash SWIFT_PACKAGE_DIR="${BUILD_DIR%Build/*}SourcePackages/artifacts" -SWIFTLINT_CMD=$(ls "$SWIFT_PACKAGE_DIR"/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-*/bin/swiftlint | head -n 1) +SWIFTLINT_CMD=$(ls "$SWIFT_PACKAGE_DIR"/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-*-macos/bin/swiftlint | head -n 1) if test -f "$SWIFTLINT_CMD" 2>&1 then From 9aacdb91d60e514f9420797994b2f2886f662682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 22:37:03 +0100 Subject: [PATCH 205/260] Use sender's name and email to commit release updates --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11e5646c06..7b3671f350 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,8 +35,8 @@ jobs: sed -i -e '3s/.*/ version = "${{ inputs.version }}",/' MODULE.bazel - name: Configure author run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "${{ github.event.sender.name }}" + git config --local user.email "${{ github.event.sender.email }}" - name: Commit changes id: pre_release run: | From 192e7eafa037db566d30201a9d8e4edc00eac3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 22:38:43 +0100 Subject: [PATCH 206/260] Create release in the sender's name --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b3671f350..81fa92afca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,10 +114,15 @@ jobs: git tag -a "${{ inputs.version }}" -m "${{ inputs.title }}" git push origin HEAD git push origin "${{ inputs.version }}" + - name: Retrieve author in uppercase + id: retrieve_author + run: | + AUTHOR=$(echo ${{ github.event.sender.login }} | tr '[:lower:]' '[:upper:]') + echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Create release run: ./tools/create-github-release.sh "${{ inputs.version }}" env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} - name: Add new changelog section run: | ./tools/add-new-changelog-section.sh From b3ba86f41b752cec67fd1e03932d3737f7d5f4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 22:39:12 +0100 Subject: [PATCH 207/260] Automate Homebrew formula bump --- .github/workflows/post-release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 5a155f496d..4648396fdf 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -45,3 +45,17 @@ jobs: "tag": "${{ github.event.release.tag_name }}", "checksum": "${{ steps.parse_checksum.outputs.checksum }}" } + bump-homebrew: + name: Bump Homebrew Formula + runs-on: ubuntu-24.04 + steps: + - name: Retrieve author in uppercase + id: retrieve_author + run: | + AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') + echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + - name: Update Homebrew formula + uses: Homebrew/actions/bump-packages@master + with: + token: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} + formulae: swiftlint From 5ad27ddbd9a0163e2daae18547049970489831f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 22:55:18 +0100 Subject: [PATCH 208/260] Revert "Use sender's name and email to commit release updates" This reverts commit 9aacdb91d60e514f9420797994b2f2886f662682. --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81fa92afca..ad18752869 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,8 +35,8 @@ jobs: sed -i -e '3s/.*/ version = "${{ inputs.version }}",/' MODULE.bazel - name: Configure author run: | - git config --local user.name "${{ github.event.sender.name }}" - git config --local user.email "${{ github.event.sender.email }}" + git config --local user.name "github-actions[bot]" + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Commit changes id: pre_release run: | From dfc84cc75bb3d4357b1869c68b3f1db218ac159d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 13 Jan 2025 22:59:01 +0100 Subject: [PATCH 209/260] Prefer `actor` over `sender` --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad18752869..bf76bd7c6c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,7 +117,7 @@ jobs: - name: Retrieve author in uppercase id: retrieve_author run: | - AUTHOR=$(echo ${{ github.event.sender.login }} | tr '[:lower:]' '[:upper:]') + AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Create release run: ./tools/create-github-release.sh "${{ inputs.version }}" From 651776896e41690f395508910fbdc9a65417c5c9 Mon Sep 17 00:00:00 2001 From: Riley Williams Date: Mon, 13 Jan 2025 18:15:40 -0500 Subject: [PATCH 210/260] Remove lints for `redundant_sendable` on protocols (#5959) --- CHANGELOG.md | 4 ++++ .../Rules/Lint/RedundantSendableRule.swift | 13 +++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa4b6e606..b2106fbc49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5952](https://github.com/realm/SwiftLint/issues/5952) +* Remove lints for `redundant_sendable` on protocols, where `Sendable` is not redundant. + [riley-williams](https://github.com/riley-williams) + [#5958](https://github.com/realm/SwiftLint/issues/5958) + ## 0.58.0: New Year’s Fresh Fold #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift index 15655bc787..d5974b01c1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift @@ -15,6 +15,7 @@ struct RedundantSendableRule: Rule { Example("actor A {}"), Example("@MainActor struct S {}"), Example("@MyActor enum E: Sendable { case a }"), + Example("@MainActor protocol P: Sendable {}"), ], triggeringExamples: [ Example("@MainActor struct ↓S: Sendable {}"), @@ -36,8 +37,8 @@ struct RedundantSendableRule: Rule { actor A: B, C // comment {} """), - Example("@MainActor protocol P: A, Sendable {}"): - Example("@MainActor protocol P: A {}"), + Example("@MainActor struct P: A, Sendable {}"): + Example("@MainActor struct P: A {}"), ] ) } @@ -58,10 +59,6 @@ private extension RedundantSendableRule { collectViolations(in: node) } - override func visitPost(_ node: ProtocolDeclSyntax) { - collectViolations(in: node) - } - override func visitPost(_ node: StructDeclSyntax) { collectViolations(in: node) } @@ -90,10 +87,6 @@ private extension RedundantSendableRule { super.visit(removeRedundantSendable(from: node)) } - override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax { - super.visit(removeRedundantSendable(from: node)) - } - override func visit(_ node: StructDeclSyntax) -> DeclSyntax { super.visit(removeRedundantSendable(from: node)) } From b1fac0d7ee5aa365558449738aa813394c12f71b Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 14 Jan 2025 11:37:25 -0800 Subject: [PATCH 211/260] Fix CryptoSwift checksum (#5961) Missed in https://github.com/realm/SwiftLint/commit/4189010afb8bd04c9d2e5f9a38fe44808e6727ad. --- bazel/repos.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazel/repos.bzl b/bazel/repos.bzl index 53d99188ea..f200a8023f 100644 --- a/bazel/repos.bzl +++ b/bazel/repos.bzl @@ -58,7 +58,7 @@ def swiftlint_repos(bzlmod = False): http_archive( name = "com_github_krzyzanowskim_cryptoswift", - sha256 = "3d649cccfe9ae0572163cde0201f013d10349a035c15225e7a4bd83c85fb0d1d", + sha256 = "69b23102ff453990d03aff4d3fabd172d0667b2b3ed95730021d60a0f8d50d14", build_file = "@SwiftLint//bazel:CryptoSwift.BUILD", strip_prefix = "CryptoSwift-1.8.4", url = "https://github.com/krzyzanowskim/CryptoSwift/archive/refs/tags/1.8.4.tar.gz", From 079c52d93a5e1961a90f52f90b733b4d479dbfed Mon Sep 17 00:00:00 2001 From: Riley Williams Date: Tue, 14 Jan 2025 14:53:19 -0500 Subject: [PATCH 212/260] Apply Markdown auto-formatting (#5960) Used https://github.com/DavidAnson/markdownlint-cli2. --- CHANGELOG.md | 322 +++++++++++++++++++++++++-------------------------- README.md | 2 +- README_CN.md | 27 +++-- README_KR.md | 19 +-- 4 files changed, 182 insertions(+), 188 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2106fbc49..4e8088b6c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ * The command plugin now requires write permissions so that it works with the `--fix` option without an error. [SimplyDanny](https://github.com/SimplyDanny) -* The artifact bundle name has changed. `SwiftLintBinary-macos.artifactbundle.zip` is now called +* The artifact bundle name has changed. `SwiftLintBinary-macos.artifactbundle.zip` is now called `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. [Bradley Mackey](https://github.com/bradleymackey) [#5514](https://github.com/realm/SwiftLint/issues/5514) @@ -223,7 +223,7 @@ [#5711](https://github.com/realm/SwiftLint/issues/5711) * Fixes `file_name` rule to match fully-qualified names of nested types. - Additionally adds a `require_fully_qualified_names` boolean option to enforce + Additionally adds a `require_fully_qualified_names` boolean option to enforce that file names match nested types only using their fully-qualified name. [fraioli](https://github.com/fraioli) [#5840](https://github.com/realm/SwiftLint/issues/5840) @@ -682,7 +682,7 @@ * Refine violation position of `trailing_closure` rule. [SimplyDanny](https://github.com/SimplyDanny) -* Trigger on the declaration keyword (i.e. `let`, `var`, `func`, `subscript`) +* Trigger on the declaration keyword (i.e. `let`, `var`, `func`, `subscript`) instead of the `static` or `class` keywords in the `explicit_acl` rule. [SimplyDanny](https://github.com/SimplyDanny) @@ -1376,7 +1376,7 @@ [AndrewDMontgomery](https://github.com/andrewdmontgomery) [#4875](https://github.com/realm/SwiftLint/pull/4875) -* Prepend `warning: ` to error messages so that they show in Xcode. +* Prepend `warning:` to error messages so that they show in Xcode. [whiteio](https://github.com/whiteio) [#4923](https://github.com/realm/SwiftLint/issues/4923) @@ -1773,149 +1773,149 @@ * Rewrite some rules with SwiftSyntax, fixing some false positives and catching more violations: - - `anonymous_argument_in_multiline_closure` - - `array_init` - - `attributes` - - `balanced_xctest_lifecycle` - - `block_based_kvo` - - `class_delegate_protocol` - - `closing_brace` - - `closure_body_length` - - `closure_parameter_position` - - `collection_alignment` - - `comment_spacing` - - `computed_accessors_order` - - `conditional_returns_on_newline` - - `contains_over_filter_count` - - `contains_over_filter_is_empty` - - `contains_over_first_not_nil` - - `contains_over_range_nil_comparison` - - `convenience_type` - - `deployment_target` - - `discarded_notification_center_observer` - - `discouraged_assert` - - `discouraged_direct_init` - - `discouraged_none_name` - - `discouraged_object_literal` - - `discouraged_optional_boolean` - - `duplicate_enum_cases` - - `duplicated_key_in_dictionary_literal` - - `dynamic_inline` - - `empty_collection_literal` - - `empty_count` - - `empty_enum_arguments` - - `empty_parameters` - - `empty_parentheses_with_trailing_closure` - - `empty_string` - - `enum_case_associated_values_count` - - `explicit_enum_raw_value` - - `explicit_init` - - `explicit_top_level_acl` - - `fallthrough` - - `file_name` - - `first_where` - - `flatmap_over_map_reduce` - - `for_where` - - `force_try` - - `force_unwrapping` - - `function_body_length` - - `function_default_parameter_at_end` - - `function_parameter_count` - - `generic_type_name` - - `ibinspectable_in_extension` - - `identical_operands` - - `implicit_getter` - - `implicitly_unwrapped_optional` - - `inclusive_language` - - `inert_defer` - - `is_disjoint` - - `joined_default_parameter` - - `large_tuple` - - `last_where` - - `legacy_cggeometry_functions` - - `legacy_constant` - - `legacy_constructor` - - `legacy_hashing` - - `legacy_multiple` - - `legacy_nsgeometry_functions` - - `legacy_objc_type` - - `legacy_random` - - `lower_acl_than_parent` - - `multiline_arguments_brackets` - - `multiline_parameters` - - `multiple_closures_with_trailing_closure` - - `no_extension_access_modifier` - - `no_fallthrough_only` - - `no_space_in_method_call` - - `notification_center_detachment` - - `nslocalizedstring_key` - - `nslocalizedstring_require_bundle` - - `nsobject_prefer_isequal` - - `number_separator` - - `object_literal` - - `operator_whitespace` - - `optional_enum_case_matching` - - `orphaned_doc_comment` - - `overridden_super_call` - - `override_in_extension` - - `pattern_matching_keywords` - - `prefer_nimble` - - `prefer_self_in_static_references` - - `prefer_self_type_over_type_of_self` - - `prefer_zero_over_explicit_init` - - `prefixed_toplevel_constant` - - `private_action` - - `private_outlet` - - `private_over_fileprivate` - - `private_subject` - - `private_unit_test` - - `prohibited_interface_builder` - - `prohibited_super_call` - - `protocol_property_accessors_order` - - `quick_discouraged_focused_test` - - `quick_discouraged_pending_test` - - `raw_value_for_camel_cased_codable_enum` - - `reduce_boolean` - - `reduce_into` - - `redundant_discardable_let` - - `redundant_nil_coalescing` - - `redundant_objc_attribute` - - `redundant_optional_initialization` - - `redundant_set_access_control` - - `redundant_string_enum_value` - - `required_deinit` - - `required_enum_case` - - `return_arrow_whitespace` - - `self_in_property_initialization` - - `shorthand_operator` - - `single_test_class` - - `sorted_first_last` - - `static_operator` - - `strict_fileprivate` - - `strong_iboutlet` - - `switch_case_alignment` - - `switch_case_on_newline` - - `test_case_accessibility` - - `toggle_bool` - - `trailing_comma` - - `trailing_semicolon` - - `type_body_length` - - `type_name` - - `unneeded_break_in_switch` - - `unneeded_parentheses_in_closure_argument` - - `unowned_variable_capture` - - `untyped_error_in_catch` - - `unused_capture_list` - - `unused_closure_parameter` - - `unused_control_flow_label` - - `unused_enumerated` - - `unused_optional_binding` - - `unused_setter_value` - - `valid_ibinspectable` - - `vertical_parameter_alignment` - - `weak_delegate` - - `xct_specific_matcher` - - `xctfail_message` + * `anonymous_argument_in_multiline_closure` + * `array_init` + * `attributes` + * `balanced_xctest_lifecycle` + * `block_based_kvo` + * `class_delegate_protocol` + * `closing_brace` + * `closure_body_length` + * `closure_parameter_position` + * `collection_alignment` + * `comment_spacing` + * `computed_accessors_order` + * `conditional_returns_on_newline` + * `contains_over_filter_count` + * `contains_over_filter_is_empty` + * `contains_over_first_not_nil` + * `contains_over_range_nil_comparison` + * `convenience_type` + * `deployment_target` + * `discarded_notification_center_observer` + * `discouraged_assert` + * `discouraged_direct_init` + * `discouraged_none_name` + * `discouraged_object_literal` + * `discouraged_optional_boolean` + * `duplicate_enum_cases` + * `duplicated_key_in_dictionary_literal` + * `dynamic_inline` + * `empty_collection_literal` + * `empty_count` + * `empty_enum_arguments` + * `empty_parameters` + * `empty_parentheses_with_trailing_closure` + * `empty_string` + * `enum_case_associated_values_count` + * `explicit_enum_raw_value` + * `explicit_init` + * `explicit_top_level_acl` + * `fallthrough` + * `file_name` + * `first_where` + * `flatmap_over_map_reduce` + * `for_where` + * `force_try` + * `force_unwrapping` + * `function_body_length` + * `function_default_parameter_at_end` + * `function_parameter_count` + * `generic_type_name` + * `ibinspectable_in_extension` + * `identical_operands` + * `implicit_getter` + * `implicitly_unwrapped_optional` + * `inclusive_language` + * `inert_defer` + * `is_disjoint` + * `joined_default_parameter` + * `large_tuple` + * `last_where` + * `legacy_cggeometry_functions` + * `legacy_constant` + * `legacy_constructor` + * `legacy_hashing` + * `legacy_multiple` + * `legacy_nsgeometry_functions` + * `legacy_objc_type` + * `legacy_random` + * `lower_acl_than_parent` + * `multiline_arguments_brackets` + * `multiline_parameters` + * `multiple_closures_with_trailing_closure` + * `no_extension_access_modifier` + * `no_fallthrough_only` + * `no_space_in_method_call` + * `notification_center_detachment` + * `nslocalizedstring_key` + * `nslocalizedstring_require_bundle` + * `nsobject_prefer_isequal` + * `number_separator` + * `object_literal` + * `operator_whitespace` + * `optional_enum_case_matching` + * `orphaned_doc_comment` + * `overridden_super_call` + * `override_in_extension` + * `pattern_matching_keywords` + * `prefer_nimble` + * `prefer_self_in_static_references` + * `prefer_self_type_over_type_of_self` + * `prefer_zero_over_explicit_init` + * `prefixed_toplevel_constant` + * `private_action` + * `private_outlet` + * `private_over_fileprivate` + * `private_subject` + * `private_unit_test` + * `prohibited_interface_builder` + * `prohibited_super_call` + * `protocol_property_accessors_order` + * `quick_discouraged_focused_test` + * `quick_discouraged_pending_test` + * `raw_value_for_camel_cased_codable_enum` + * `reduce_boolean` + * `reduce_into` + * `redundant_discardable_let` + * `redundant_nil_coalescing` + * `redundant_objc_attribute` + * `redundant_optional_initialization` + * `redundant_set_access_control` + * `redundant_string_enum_value` + * `required_deinit` + * `required_enum_case` + * `return_arrow_whitespace` + * `self_in_property_initialization` + * `shorthand_operator` + * `single_test_class` + * `sorted_first_last` + * `static_operator` + * `strict_fileprivate` + * `strong_iboutlet` + * `switch_case_alignment` + * `switch_case_on_newline` + * `test_case_accessibility` + * `toggle_bool` + * `trailing_comma` + * `trailing_semicolon` + * `type_body_length` + * `type_name` + * `unneeded_break_in_switch` + * `unneeded_parentheses_in_closure_argument` + * `unowned_variable_capture` + * `untyped_error_in_catch` + * `unused_capture_list` + * `unused_closure_parameter` + * `unused_control_flow_label` + * `unused_enumerated` + * `unused_optional_binding` + * `unused_setter_value` + * `valid_ibinspectable` + * `vertical_parameter_alignment` + * `weak_delegate` + * `xct_specific_matcher` + * `xctfail_message` [Marcelo Fabri](https://github.com/marcelofabri) [SimplyDanny](https://github.com/SimplyDanny) @@ -1958,7 +1958,7 @@ `if let self {}`) when using a `bind_identifier` different than `self`. [Marcelo Fabri](https://github.com/marcelofabri) -* Add `library_content_provider` file type to `file_types_order` rule +* Add `library_content_provider` file type to `file_types_order` rule to allow `LibraryContentProvider` to be ordered independent from `main_type`. [dahlborn](https://github.com/dahlborn) @@ -6673,14 +6673,14 @@ The next release will require Swift 4.0 or higher to build. * Add `attributes` opt-in rule which validates if an attribute (`@objc`, `@IBOutlet`, `@discardableResult`, etc) is in the right position: - - If the attribute is `@IBAction` or `@NSManaged`, it should always be on + * If the attribute is `@IBAction` or `@NSManaged`, it should always be on the same line as the declaration - - If the attribute has parameters, it should always be on the line above + * If the attribute has parameters, it should always be on the line above the declaration - - Otherwise: - - if the attribute is applied to a variable, it should be on the same line - - if it's applied to a type or function, it should be on the line above - - if it's applied to an import (the only option is `@testable import`), + * Otherwise: + * if the attribute is applied to a variable, it should be on the same line + * if it's applied to a type or function, it should be on the line above + * if it's applied to an import (the only option is `@testable import`), it should be on the same line. You can also configure what attributes should be always on a new line or on the same line as the declaration with the `always_on_same_line` and @@ -6715,8 +6715,8 @@ The next release will require Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) [#964](https://github.com/realm/SwiftLint/issues/964) -* Add correctable `empty_parameters` rule to validate usage of `() -> ` - over `Void -> `. +* Add correctable `empty_parameters` rule to validate usage of `() ->` + over `Void ->`. [Marcelo Fabri](https://github.com/marcelofabri) [#573](https://github.com/realm/SwiftLint/issues/573) @@ -7608,7 +7608,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#316](https://github.com/realm/SwiftLint/issues/316) - ## 0.5.4: Bounce™ #### Breaking @@ -7647,7 +7646,6 @@ This release has seen a phenomenal uptake in community contributions! [Norio Nomura](https://github.com/norio-nomura) [#295](https://github.com/realm/SwiftLint/issues/295) - ## 0.5.3: Mountain Scent #### Breaking @@ -7689,7 +7687,6 @@ This release has seen a phenomenal uptake in community contributions! [diogoguimaraes](https://github.com/diogoguimaraes) [#267](https://github.com/realm/SwiftLint/issues/267) - ## 0.5.1: Lint Tray Malfunction #### Breaking @@ -7712,7 +7709,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#264](https://github.com/realm/SwiftLint/issues/264) - ## 0.5.0: Downy™ #### Breaking @@ -7752,7 +7748,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#234](https://github.com/realm/SwiftLint/issues/234) - ## 0.4.0: Wrinkle Release #### Breaking @@ -7812,7 +7807,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#200](https://github.com/realm/SwiftLint/issues/200) - ## 0.3.0: Wrinkly Rules #### Breaking @@ -7865,7 +7859,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#154](https://github.com/realm/SwiftLint/issues/154) - ## 0.2.0: Tumble Dry #### Breaking @@ -7921,7 +7914,6 @@ This release has seen a phenomenal uptake in community contributions! * Allow newlines in function return arrow. [JP Simard](https://github.com/jpsim) - ## 0.1.2: FabricSoftenerRule #### Breaking @@ -7953,7 +7945,6 @@ This release has seen a phenomenal uptake in community contributions! * None. - ## 0.1.1: Top Loading #### Breaking @@ -8001,7 +7992,6 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#43](https://github.com/realm/SwiftLint/issues/43) - ## 0.1.0: Fresh Out Of The Dryer First Version! diff --git a/README.md b/README.md index c2b70e245f..282de73915 100644 --- a/README.md +++ b/README.md @@ -860,7 +860,7 @@ All syntax kinds used in a snippet of Swift code can be extracted asking which match to `keyword` and `identifier` in the above list. -If using custom rules in combination with `only_rules`, you must include the +If using custom rules in combination with `only_rules`, you must include the literal string `custom_rules` in the `only_rules` list: ```yaml diff --git a/README_CN.md b/README_CN.md index a1593f6e51..1192cff3f9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -13,6 +13,7 @@ SwiftLint Hook 了 [Clang](http://clang.llvm.org) 和 [SourceKit](http://www.jps 不可接受的行为报告给 [info@realm.io](mailto:info@realm.io)。 ## 安装 + ### 使用[Swift Package Manager](https://github.com/apple/swift-package-manager) SwiftLint 可以用作[命令插件](#swift-package-command-plugin)或[构建工具插件](#build-tool-plugins) @@ -31,7 +32,6 @@ SwiftLint 可以用作[命令插件](#swift-package-command-plugin)或[构建工 其中,用所需的最低版本或精确版本替换 ``。 - ### [Xcode Package Dependency](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) 使用以下链接将 SwiftLint 作为包依赖添加到 Xcode 项目中: @@ -40,14 +40,13 @@ SwiftLint 可以用作[命令插件](#swift-package-command-plugin)或[构建工 https://github.com/SimplyDanny/SwiftLintPlugins ``` - -### 使用 [Homebrew](http://brew.sh/): +### 使用 [Homebrew](http://brew.sh/) ``` brew install swiftlint ``` -### 使用 [CocoaPods](https://cocoapods.org): +### 使用 [CocoaPods](https://cocoapods.org) 将如下代码添加到你的 Podfile 即可: @@ -61,16 +60,17 @@ pod 'SwiftLint' 请注意这会将 SwiftLint 二进制文件、所依赖的二进制文件和 Swift 二进制库安装到 `Pods/` 目录下,所以不推荐将此目录添加到版本控制系统(如 git)中进行跟踪。 -### 使用 [Mint](https://github.com/yonaskolb/mint): +### 使用 [Mint](https://github.com/yonaskolb/mint) + ``` -$ mint install realm/SwiftLint +mint install realm/SwiftLint ``` -### 使用安装包: +### 使用安装包 你也可以通过从[最新的 GitHub 发布地址](https://github.com/realm/SwiftLint/releases/latest)下载 `SwiftLint.pkg` 然后执行的方式安装 SwiftLint。 -### 编译源代码: +### 编译源代码 你也可以通过 clone SwiftLint 的 Git 仓库到本地然后执行 `make install` (Xcode 15.0+) 以从源代码构建及安装。 @@ -142,7 +142,6 @@ swiftlint_deps() bazel run -c opt @SwiftLint//:swiftlint ``` - ## 用法 ### 报告 @@ -164,7 +163,7 @@ Xcode 15 对 Build Settings 进行了重大更改,它将 `ENABLE_USER_SCRIPT_S 如果你是在搭载 Apple 芯片的 Mac 上通过 Homebrew 安装的 SwiftLint,你可能会遇到这个警告: -> warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint +> warning: SwiftLint not installed, download from 这是因为 Homebrew 在搭载 Apple 芯片的 Mac 上将二进制文件默认安装到了 `/opt/homebrew/bin` 下。如果要让 Xcode 知道 SwiftLint 在哪,你可以在 Build Phase 中将 @@ -227,7 +226,7 @@ Xcode 构建工具插件。 对于无人值守的使用场景(例如在 CI 上),可以通过以下方式禁用软件包和宏的验证对话框 * 单独将 `-skipPackagePluginValidation` 和 `-skipMacroValidation` 传递到 `xcodebuild` 或者 -* 为那个用户使用 `defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES` 进行全局设置,然后写入 `defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES` +* 为那个用户使用 `defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES` 进行全局设置,然后写入 `defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES` _注意:这将隐含地信任所有的Xcode软件包插件,并绕过Xcode的软件包验证对话框。 这对安全有影响。_ @@ -278,16 +277,19 @@ swiftlint( `swiftlint` 也可以在 [Docker](https://www.docker.com/) 上使用 `Ubuntu` 作为一个镜像使用。 因此,第一次你需要使用下面的命令调用 docker 镜像: + ```bash docker pull ghcr.io/realm/swiftlint:latest ``` 接下来,你只需在 docker 中运行`swiftlint`: + ```bash docker run -it -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest ``` 这将在你现在所在的文件夹(`pwd`)中执行`swiftlint`,显示类似的输出: + ```bash $ docker run -it -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest Linting Swift files in current working directory @@ -344,7 +346,7 @@ SwiftLint 工作于 SourceKit 这一层,所以 Swift 版本发生变化时它 你可能也给反向 DNS 符号设置了 `TOOLCHAINS` 环境变量来标记一个特定的 Swift 工具集版本: ```shell -$ TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect +TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect ``` 在 Linux 上,SourceKit 默认需要位于 `/usr/lib/libsourcekitdInProc.so` 或者通过 `LINUX_SOURCEKIT_LIB_PATH` 环境变量进行指定。 @@ -366,6 +368,7 @@ repos: 将 `rev` 调整为您选择的 SwiftLint 版本。可以使用 `pre-commit autoupdate` 来更新到当前版本。 SwiftLint 可以使用 `entry` 进行配置以应用修复和报错: + ```yaml - repo: https://github.com/realm/SwiftLint rev: 0.50.3 diff --git a/README_KR.md b/README_KR.md index 173a10ad02..367bcd2a3b 100644 --- a/README_KR.md +++ b/README_KR.md @@ -13,13 +13,13 @@ SwiftLint는 좀 더 정확한 결과를 위해 [Clang](http://clang.llvm.org) ## 설치 방법 -### [Homebrew](http://brew.sh/)를 사용하는 경우: +### [Homebrew](http://brew.sh/)를 사용하는 경우 ``` brew install swiftlint ``` -### [CocoaPods](https://cocoapods.org)를 사용하는 경우: +### [CocoaPods](https://cocoapods.org)를 사용하는 경우 Podfile에 아래 라인을 추가하기만 하면 됩니다. @@ -33,16 +33,17 @@ CocoaPods를 사용하면 최신 버전 외에도 SwiftLint의 특정 버전을 이렇게 했을 때 SwiftLint 바이너리 및 그에 종속된 바이너리들과 스위프트 바이너리까지 `Pods/` 디렉터리에 추가되기 때문에, git 등의 SCM에 이런 디렉터리들을 체크인하는 것은 권장하지 않습니다. -### [Mint](https://github.com/yonaskolb/mint)를 사용하는 경우: +### [Mint](https://github.com/yonaskolb/mint)를 사용하는 경우 + ``` -$ mint install realm/SwiftLint +mint install realm/SwiftLint ``` -### 빌드된 패키지를 사용하는 경우: +### 빌드된 패키지를 사용하는 경우 [최신 깃허브 릴리즈](https://github.com/realm/SwiftLint/releases/latest)에서 `SwiftLint.pkg`를 다운로드해서 설치하고 실행할 수 있습니다. -### 소스를 직접 컴파일하는 경우: +### 소스를 직접 컴파일하는 경우 본 프로젝트를 클론해서 빌드할 수도 있습니다. `make install` 명령을 사용합니다. (Xcode 12.5 이후 버전) @@ -70,7 +71,7 @@ fi 만약, 애플 실리콘 환경에서 Homebrew를 통해 SwiftLint를 설치했다면, 아마도 다음과 같은 경고를 겪었을 것입니다. -> warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint +> warning: SwiftLint not installed, download from 그 이유는, 애플 실리콘 기반 맥에서 Homebrew는 기본적으로 바이너리들을 `/opt/homebrew/bin`에 저장하기 때문입니다. SwiftLint가 어디 있는지 찾는 것을 Xcode에 알려주기 위해, build phase에서 `/opt/homebrew/bin`를 `PATH` 환경 변수에 동시에 추가하여야 합니다. @@ -159,7 +160,7 @@ SwiftLint는 SourceKit에 연결되어 있으므로 스위프트 언어가 변 SwiftLint를 실행할 때는 항상 스위프트 파일을 컴파일하는 동일한 툴체인을 사용해야 합니다. -설치된 툴체인이나 Xcode가 여러 개인 경우 혹은 스위프트 구 버전을 사용해야 하는 경우(Xcode 8에서 스위프트 2.3 버전을 사용하는 경우)에는 SwiftLint의 기본 스위프트 툴체인을 변경해야 할 수도 있습니다. +설치된 툴체인이나 Xcode가 여러 개인 경우 혹은 스위프트 구 버전을 사용해야 하는 경우(Xcode 8에서 스위프트 2.3 버전을 사용하는 경우)에는 SwiftLint의 기본 스위프트 툴체인을 변경해야 할 수도 있습니다. SwiftLint가 어느 스위프트 툴체인을 사용할지 결정하는 순서는 다음과 같습니다. @@ -176,7 +177,7 @@ SwiftLint가 어느 스위프트 툴체인을 사용할지 결정하는 순서 `TOOLCHAINS` 환경 변수에 스위프트 툴체인 버전을 식별할 수 있는 값을 리버스 DNS 형식으로 지정할 수도 있습니다. ```shell -$ TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect +TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect ``` 리눅스에서는 SourceKit이 `/usr/lib/libsourcekitdInProc.so` 혹은 `LINUX_SOURCEKIT_LIB_PATH` 환경변수로 지정된 경로에 존재해야 합니다. From 9b22cda361285fd084c1ea27c1440f3245a8154c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 14 Jan 2025 21:50:11 +0100 Subject: [PATCH 213/260] Add `ib_segue_action` to default configuration (#5956) --- CHANGELOG.md | 4 ++++ .../RuleConfigurations/TypeContentsOrderConfiguration.swift | 2 +- Tests/IntegrationTests/default_rule_configurations.yml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8088b6c9..542931ed47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ [riley-williams](https://github.com/riley-williams) [#5958](https://github.com/realm/SwiftLint/issues/5958) +* Add `ib_segue_action` to default configuration of `type_contents_order` rule on the same level as `ib_action` to define and document a standard position. + [SimplyDanny](https://github.com/SimplyDanny) + [#5524](https://github.com/realm/SwiftLint/issues/5524) + ## 0.58.0: New Year’s Fresh Fold #### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift index 9587d948b6..645cd52ad8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/TypeContentsOrderConfiguration.swift @@ -38,7 +38,7 @@ struct TypeContentsOrderConfiguration: SeverityBasedRuleConfiguration { [.initializer], [.typeMethod], [.viewLifeCycleMethod], - [.ibAction], + [.ibAction, .ibSegueAction], [.otherMethod], [.subscript], [.deinitializer], diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 76cefae7c2..5d667a9847 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -988,7 +988,7 @@ type_body_length: opt-in: false type_contents_order: severity: warning - order: [[case], [type_alias, associated_type], [subtype], [type_property], [instance_property], [ib_inspectable], [ib_outlet], [initializer], [type_method], [view_life_cycle_method], [ib_action], [other_method], [subscript], [deinitializer]] + order: [[case], [type_alias, associated_type], [subtype], [type_property], [instance_property], [ib_inspectable], [ib_outlet], [initializer], [type_method], [view_life_cycle_method], [ib_action, ib_segue_action], [other_method], [subscript], [deinitializer]] meta: opt-in: true type_name: From fcdc98a52daf9f86fdf961a6b3e64678b6d003ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 15 Jan 2025 19:15:44 +0100 Subject: [PATCH 214/260] Revert "Improve performance of excluded files filter" (#5962) This reverts commit 152355e36f97ef5cf1c420181237f2e89e653b28 from #5157. # Conflicts: # tools/oss-check --- BUILD | 1 - CHANGELOG.md | 6 ++ MODULE.bazel | 1 - Package.resolved | 9 --- Package.swift | 2 - .../Extensions/String+SwiftLint.swift | 9 --- .../Configuration+CommandLine.swift | 23 +++--- .../Configuration+LintableFiles.swift | 80 ++++++++++++------- Source/SwiftLintFramework/Helpers/Glob.swift | 23 ------ Tests/FrameworkTests/ConfigurationTests.swift | 37 +++++---- Tests/FrameworkTests/GlobTests.swift | 27 ------- Tests/IntegrationTests/IntegrationTests.swift | 4 +- bazel/repos.bzl | 7 -- 13 files changed, 97 insertions(+), 132 deletions(-) diff --git a/BUILD b/BUILD index 53907b3b7b..1174d12b31 100644 --- a/BUILD +++ b/BUILD @@ -94,7 +94,6 @@ swift_library( ":SourceKittenFramework.wrapper", "@sourcekitten_com_github_jpsim_yams//:Yams", "@swiftlint_com_github_scottrhoyt_swifty_text_table//:SwiftyTextTable", - "@com_github_ileitch_swift-filename-matcher//:FilenameMatcher" ] + select({ "@platforms//os:linux": ["@com_github_krzyzanowskim_cryptoswift//:CryptoSwift"], "//conditions:default": [":DyldWarningWorkaround"], diff --git a/CHANGELOG.md b/CHANGELOG.md index 542931ed47..e245749a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,12 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5954](https://github.com/realm/SwiftLint/issues/5954) +* Revert changes to improve performance when exclude patterns resolve to a large set of files. While resolving files + indeed got much faster in certain setups, it leads to missed exclusions for nested configurations and when the linted + folder is not the current folder. + [SimplyDanny](https://github.com/SimplyDanny) + [#5953](https://github.com/realm/SwiftLint/issues/5953) + #### Experimental * None. diff --git a/MODULE.bazel b/MODULE.bazel index 9b834ef077..aee479cf91 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,7 +14,6 @@ bazel_dep(name = "sourcekitten", version = "0.36.0", repo_name = "com_github_jps bazel_dep(name = "swift-syntax", version = "600.0.0", repo_name = "SwiftSyntax") bazel_dep(name = "swift_argument_parser", version = "1.3.1.1", repo_name = "sourcekitten_com_github_apple_swift_argument_parser") bazel_dep(name = "yams", version = "5.1.3", repo_name = "sourcekitten_com_github_jpsim_yams") -bazel_dep(name = "swift-filename-matcher", version = "2.0.0", repo_name = "com_github_ileitch_swift-filename-matcher") swiftlint_repos = use_extension("//bazel:repos.bzl", "swiftlint_repos_bzlmod") use_repo( diff --git a/Package.resolved b/Package.resolved index b5cb643c48..baa9895fe5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -36,15 +36,6 @@ "version" : "1.3.1" } }, - { - "identity" : "swift-filename-matcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ileitch/swift-filename-matcher", - "state" : { - "revision" : "516ff95f6a06c7a9eff8e944e989c7af076c5cdb", - "version" : "2.0.0" - } - }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index f698365767..5f66e74579 100644 --- a/Package.swift +++ b/Package.swift @@ -37,7 +37,6 @@ let package = Package( .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.2.0"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.8.4")), - .package(url: "https://github.com/ileitch/swift-filename-matcher", .upToNextMinor(from: "2.0.0")), ], targets: [ .executableTarget( @@ -92,7 +91,6 @@ let package = Package( .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), .product(name: "SwiftyTextTable", package: "SwiftyTextTable"), .product(name: "Yams", package: "Yams"), - .product(name: "FilenameMatcher", package: "swift-filename-matcher"), "SwiftLintCoreMacros", ], swiftSettings: swiftFeatures + strictConcurrency diff --git a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift index 73d93adf9f..49839438b7 100644 --- a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift @@ -68,20 +68,11 @@ public extension String { /// Returns a new string, converting the path to a canonical absolute path. /// - /// > Important: This method might use an incorrect working directory internally. This can cause test failures - /// in Bazel builds but does not seem to cause trouble in production. - /// /// - returns: A new `String`. func absolutePathStandardized() -> String { bridge().absolutePathRepresentation().bridge().standardizingPath } - /// Like ``absolutePathStandardized()`` but with the working directory that's used everywhere else. - var normalized: String { - let cwd = FileManager.default.currentDirectoryPath.bridge().standardizingPath - return bridge().absolutePathRepresentation(rootDirectory: cwd) - } - var isFile: Bool { if self.isEmpty { return false diff --git a/Source/SwiftLintFramework/Configuration+CommandLine.swift b/Source/SwiftLintFramework/Configuration+CommandLine.swift index 987542a5be..c24f5c9c55 100644 --- a/Source/SwiftLintFramework/Configuration+CommandLine.swift +++ b/Source/SwiftLintFramework/Configuration+CommandLine.swift @@ -257,12 +257,15 @@ extension Configuration { guard options.forceExclude else { return files } + let scriptInputPaths = files.compactMap(\.path) - return ( - visitor.options.useExcludingByPrefix - ? filterExcludedPathsByPrefix(in: scriptInputPaths) - : filterExcludedPaths(in: scriptInputPaths) - ).map(SwiftLintFile.init(pathDeferringReading:)) + + if options.useExcludingByPrefix { + return filterExcludedPathsByPrefix(in: scriptInputPaths) + .map(SwiftLintFile.init(pathDeferringReading:)) + } + return filterExcludedPaths(excludedPaths(), in: scriptInputPaths) + .map(SwiftLintFile.init(pathDeferringReading:)) } if !options.quiet { let filesInfo: String @@ -274,12 +277,14 @@ extension Configuration { queuedPrintError("\(options.capitalizedVerb) Swift files \(filesInfo)") } - return visitor.options.paths.flatMap { + let excludeLintableFilesBy = options.useExcludingByPrefix + ? Configuration.ExcludeBy.prefix + : .paths(excludedPaths: excludedPaths()) + return options.paths.flatMap { self.lintableFiles( inPath: $0, - forceExclude: visitor.options.forceExclude, - excludeByPrefix: visitor.options.useExcludingByPrefix - ) + forceExclude: options.forceExclude, + excludeBy: excludeLintableFilesBy) } } diff --git a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift index 67dc6d00ce..30ea7be975 100644 --- a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift @@ -1,21 +1,25 @@ import Foundation extension Configuration { - // MARK: Lintable Paths + public enum ExcludeBy { + case prefix + case paths(excludedPaths: [String]) + } + // MARK: Lintable Paths /// Returns the files that can be linted by SwiftLint in the specified parent path. /// /// - parameter path: The parent path in which to search for lintable files. Can be a directory or a /// file. /// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if /// `path` is an exact match. - /// - parameter excludeByPrefix: Whether or not it uses the exclude-by-prefix algorithm. + /// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorithm. /// /// - returns: Files to lint. public func lintableFiles(inPath path: String, forceExclude: Bool, - excludeByPrefix: Bool) -> [SwiftLintFile] { - lintablePaths(inPath: path, forceExclude: forceExclude, excludeByPrefix: excludeByPrefix) + excludeBy: ExcludeBy) -> [SwiftLintFile] { + lintablePaths(inPath: path, forceExclude: forceExclude, excludeBy: excludeBy) .compactMap(SwiftLintFile.init(pathDeferringReading:)) } @@ -25,21 +29,24 @@ extension Configuration { /// file. /// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if /// `path` is an exact match. - /// - parameter excludeByPrefix: Whether or not it uses the exclude-by-prefix algorithm. + /// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorithm. /// - parameter fileManager: The lintable file manager to use to search for lintable files. /// /// - returns: Paths for files to lint. internal func lintablePaths( inPath path: String, forceExclude: Bool, - excludeByPrefix: Bool, + excludeBy: ExcludeBy, fileManager: some LintableFileManager = FileManager.default ) -> [String] { if fileManager.isFile(atPath: path) { if forceExclude { - return excludeByPrefix - ? filterExcludedPathsByPrefix(in: [path.normalized]) - : filterExcludedPaths(in: [path.normalized]) + switch excludeBy { + case .prefix: + return filterExcludedPathsByPrefix(in: [path.absolutePathStandardized()]) + case .paths(let excludedPaths): + return filterExcludedPaths(excludedPaths, in: [path.absolutePathStandardized()]) + } } // If path is a file and we're not forcing excludes, skip filtering with excluded/included paths return [path] @@ -50,29 +57,35 @@ extension Configuration { .flatMap(Glob.resolveGlob) .parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) } - return excludeByPrefix - ? filterExcludedPathsByPrefix(in: pathsForPath + includedPaths) - : filterExcludedPaths(in: pathsForPath + includedPaths) + switch excludeBy { + case .prefix: + return filterExcludedPathsByPrefix(in: pathsForPath, includedPaths) + case .paths(let excludedPaths): + return filterExcludedPaths(excludedPaths, in: pathsForPath, includedPaths) + } } /// Returns an array of file paths after removing the excluded paths as defined by this configuration. /// - /// - parameter paths: The input paths to filter. + /// - parameter fileManager: The lintable file manager to use to expand the excluded paths into all matching paths. + /// - parameter paths: The input paths to filter. /// /// - returns: The input paths after removing the excluded paths. - public func filterExcludedPaths(in paths: [String]) -> [String] { + public func filterExcludedPaths( + _ excludedPaths: [String], + in paths: [String]... + ) -> [String] { + let allPaths = paths.flatMap { $0 } #if os(Linux) - let result = NSMutableOrderedSet(capacity: paths.count) - result.addObjects(from: paths) + let result = NSMutableOrderedSet(capacity: allPaths.count) + result.addObjects(from: allPaths) #else - let result = NSMutableOrderedSet(array: paths) + let result = NSMutableOrderedSet(array: allPaths) #endif - let exclusionPatterns = self.excludedPaths.flatMap { - Glob.createFilenameMatchers(root: rootDirectory, pattern: $0) - } - return result.array - .parallelCompactMap { exclusionPatterns.anyMatch(filename: $0 as! String) ? nil : $0 as? String } - // swiftlint:disable:previous force_cast + + result.minusSet(Set(excludedPaths)) + // swiftlint:disable:next force_cast + return result.map { $0 as! String } } /// Returns the file paths that are excluded by this configuration using filtering by absolute path prefix. @@ -81,12 +94,25 @@ extension Configuration { /// algorithm `filterExcludedPaths`. /// /// - returns: The input paths after removing the excluded paths. - public func filterExcludedPathsByPrefix(in paths: [String]) -> [String] { + public func filterExcludedPathsByPrefix(in paths: [String]...) -> [String] { + let allPaths = paths.flatMap { $0 } let excludedPaths = self.excludedPaths - .parallelFlatMap { Glob.resolveGlob($0) } - .map(\.normalized) - return paths.filter { path in + .parallelFlatMap { @Sendable in Glob.resolveGlob($0) } + .map { $0.absolutePathStandardized() } + return allPaths.filter { path in !excludedPaths.contains { path.hasPrefix($0) } } } + + /// Returns the file paths that are excluded by this configuration after expanding them using the specified file + /// manager. + /// + /// - parameter fileManager: The file manager to get child paths in a given parent location. + /// + /// - returns: The expanded excluded file paths. + public func excludedPaths(fileManager: some LintableFileManager = FileManager.default) -> [String] { + excludedPaths + .flatMap(Glob.resolveGlob) + .parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) } + } } diff --git a/Source/SwiftLintFramework/Helpers/Glob.swift b/Source/SwiftLintFramework/Helpers/Glob.swift index 9ef79f1ca0..4ef3cfbacf 100644 --- a/Source/SwiftLintFramework/Helpers/Glob.swift +++ b/Source/SwiftLintFramework/Helpers/Glob.swift @@ -1,4 +1,3 @@ -import FilenameMatcher import Foundation #if os(Linux) @@ -37,28 +36,6 @@ struct Glob { .map { $0.absolutePathStandardized() } } - static func createFilenameMatchers(root: String, pattern: String) -> [FilenameMatcher] { - var absolutPathPattern = pattern - if !pattern.starts(with: root) { - // If the root is not already part of the pattern, prepend it. - absolutPathPattern = root + (root.hasSuffix("/") ? "" : "/") + absolutPathPattern - } - if pattern.hasSuffix(".swift") || pattern.hasSuffix("/**") { - // Suffix is already well defined. - return [FilenameMatcher(pattern: absolutPathPattern)] - } - if pattern.hasSuffix("/") { - // Matching all files in the folder. - return [FilenameMatcher(pattern: absolutPathPattern + "**")] - } - // The pattern could match files in the last folder in the path or all contained files if the last component - // represents folders. - return [ - FilenameMatcher(pattern: absolutPathPattern), - FilenameMatcher(pattern: absolutPathPattern + "/**"), - ] - } - // MARK: Private private static func expandGlobstar(pattern: String) -> [String] { diff --git a/Tests/FrameworkTests/ConfigurationTests.swift b/Tests/FrameworkTests/ConfigurationTests.swift index 2fa222e1a2..f88852c94c 100644 --- a/Tests/FrameworkTests/ConfigurationTests.swift +++ b/Tests/FrameworkTests/ConfigurationTests.swift @@ -288,9 +288,10 @@ final class ConfigurationTests: SwiftLintTestCase { excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"] ) + let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "", forceExclude: false, - excludeByPrefix: false, + excludeBy: .paths(excludedPaths: excludedPaths), fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -298,9 +299,10 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesFile() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/ExcludedFile.swift"]) + let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory/ExcludedFile.swift", forceExclude: true, - excludeByPrefix: false, + excludeBy: .paths(excludedPaths: excludedPaths), fileManager: fileManager) XCTAssertEqual([], paths) } @@ -309,9 +311,10 @@ final class ConfigurationTests: SwiftLintTestCase { let fileManager = TestFileManager() let configuration = Configuration(includedPaths: ["directory"], excludedPaths: ["directory/ExcludedFile.swift", "directory/excluded"]) + let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "", forceExclude: true, - excludeByPrefix: false, + excludeBy: .paths(excludedPaths: excludedPaths), fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -319,9 +322,10 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesDirectory() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"]) + let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true, - excludeByPrefix: false, + excludeBy: .paths(excludedPaths: excludedPaths), fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } @@ -329,17 +333,19 @@ final class ConfigurationTests: SwiftLintTestCase { func testForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() { let fileManager = TestFileManager() let configuration = Configuration(excludedPaths: ["directory/excluded", "directory/ExcludedFile.swift"]) + let excludedPaths = configuration.excludedPaths(fileManager: fileManager) let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true, - excludeByPrefix: false, + excludeBy: .paths(excludedPaths: excludedPaths), fileManager: fileManager) XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"].absolutePathsStandardized(), paths) } func testLintablePaths() { + let excluded = Configuration.default.excludedPaths(fileManager: TestFileManager()) let paths = Configuration.default.lintablePaths(inPath: Mock.Dir.level0, forceExclude: false, - excludeByPrefix: false) + excludeBy: .paths(excludedPaths: excluded)) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() let expectedFilenames = [ "DirectoryLevel1.swift", @@ -355,7 +361,7 @@ final class ConfigurationTests: SwiftLintTestCase { let configuration = Configuration(includedPaths: ["**/Level2"]) let paths = configuration.lintablePaths(inPath: Mock.Dir.level0, forceExclude: true, - excludeByPrefix: false) + excludeBy: .paths(excludedPaths: configuration.excludedPaths)) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() let expectedFilenames = ["Level2.swift", "Level3.swift"] @@ -368,9 +374,10 @@ final class ConfigurationTests: SwiftLintTestCase { excludedPaths: [Mock.Dir.level3.stringByAppendingPathComponent("*.swift")] ) + let excludedPaths = configuration.excludedPaths() let lintablePaths = configuration.lintablePaths(inPath: "", forceExclude: false, - excludeByPrefix: false) + excludeBy: .paths(excludedPaths: excludedPaths)) XCTAssertEqual(lintablePaths, []) } @@ -485,7 +492,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: Mock.Dir.level0, forceExclude: false, - excludeByPrefix: true) + excludeBy: .prefix) let filenames = paths.map { $0.bridge().lastPathComponent } XCTAssertEqual(filenames, ["Level2.swift"]) } @@ -495,7 +502,7 @@ extension ConfigurationTests { let configuration = Configuration(excludedPaths: ["Level1/Level2/Level3/Level3.swift"]) let paths = configuration.lintablePaths(inPath: "Level1/Level2/Level3/Level3.swift", forceExclude: true, - excludeByPrefix: true) + excludeBy: .prefix) XCTAssertEqual([], paths) } @@ -505,7 +512,7 @@ extension ConfigurationTests { excludedPaths: ["Level1/Level1.swift"]) let paths = configuration.lintablePaths(inPath: "Level1", forceExclude: true, - excludeByPrefix: true) + excludeBy: .prefix) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(["Level2.swift", "Level3.swift"], filenames) } @@ -519,7 +526,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: ".", forceExclude: true, - excludeByPrefix: true) + excludeBy: .prefix) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(["Level0.swift", "Level1.swift"], filenames) } @@ -533,7 +540,7 @@ extension ConfigurationTests { ) let paths = configuration.lintablePaths(inPath: ".", forceExclude: true, - excludeByPrefix: true) + excludeBy: .prefix) let filenames = paths.map { $0.bridge().lastPathComponent } XCTAssertEqual(["Level0.swift"], filenames) } @@ -545,7 +552,7 @@ extension ConfigurationTests { excludedPaths: ["Level1/*/*.swift", "Level1/*/*/*.swift"]) let paths = configuration.lintablePaths(inPath: "Level1", forceExclude: false, - excludeByPrefix: true) + excludeBy: .prefix) let filenames = paths.map { $0.bridge().lastPathComponent }.sorted() XCTAssertEqual(filenames, ["Level1.swift"]) } @@ -599,7 +606,7 @@ extension ConfigurationTests { private extension Sequence where Element == String { func absolutePathsStandardized() -> [String] { - map(\.normalized) + map { $0.absolutePathStandardized() } } } diff --git a/Tests/FrameworkTests/GlobTests.swift b/Tests/FrameworkTests/GlobTests.swift index 11b1e17730..f4ed2f38b6 100644 --- a/Tests/FrameworkTests/GlobTests.swift +++ b/Tests/FrameworkTests/GlobTests.swift @@ -83,31 +83,4 @@ final class GlobTests: SwiftLintTestCase { let files = Glob.resolveGlob(mockPath.stringByAppendingPathComponent("**/*.swift")) XCTAssertEqual(files.sorted(), expectedFiles.sorted()) } - - func testCreateFilenameMatchers() { - func assertGlobMatch(root: String, pattern: String, filename: String) { - let matchers = Glob.createFilenameMatchers(root: root, pattern: pattern) - XCTAssert(matchers.anyMatch(filename: filename)) - } - - assertGlobMatch(root: "/a/b/", pattern: "c/*.swift", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "/a", pattern: "**/*.swift", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "/a", pattern: "**/*.swift", filename: "/a/b.swift") - assertGlobMatch(root: "", pattern: "**/*.swift", filename: "/a/b.swift") - assertGlobMatch(root: "", pattern: "a/**/b.swift", filename: "a/b.swift") - assertGlobMatch(root: "", pattern: "a/**/b.swift", filename: "a/c/b.swift") - assertGlobMatch(root: "", pattern: "**/*.swift", filename: "a.swift") - assertGlobMatch(root: "", pattern: "a/**/*.swift", filename: "a/b/c.swift") - assertGlobMatch(root: "", pattern: "a/**/*.swift", filename: "a/b.swift") - assertGlobMatch(root: "/a/b", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "/a/", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") - - assertGlobMatch(root: "", pattern: "/a/b/c", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "", pattern: "/a/b/c/", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "", pattern: "/a/b/c/*.swift", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "", pattern: "/d.swift/*.swift", filename: "/d.swift/e.swift") - assertGlobMatch(root: "", pattern: "/a/**", filename: "/a/b/c/d.swift") - assertGlobMatch(root: "", pattern: "**/*Test*", filename: "/a/b/c/MyTest2.swift") - assertGlobMatch(root: "", pattern: "**/*Test*", filename: "/a/b/MyTests/c.swift") - } } diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index 877686a120..825b869a6e 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -23,7 +23,7 @@ final class IntegrationTests: SwiftLintTestCase { let swiftFiles = config.lintableFiles( inPath: "", forceExclude: false, - excludeByPrefix: false) + excludeBy: .paths(excludedPaths: config.excludedPaths())) XCTAssert( swiftFiles.contains(where: { #filePath.bridge().absolutePathRepresentation() == $0.path }), "current file should be included" @@ -48,7 +48,7 @@ final class IntegrationTests: SwiftLintTestCase { let swiftFiles = config.lintableFiles( inPath: "", forceExclude: false, - excludeByPrefix: false) + excludeBy: .paths(excludedPaths: config.excludedPaths())) let storage = RuleStorage() let corrections = swiftFiles.parallelFlatMap { Linter(file: $0, configuration: config).collect(into: storage).correct(using: storage) diff --git a/bazel/repos.bzl b/bazel/repos.bzl index f200a8023f..592acd82f2 100644 --- a/bazel/repos.bzl +++ b/bazel/repos.bzl @@ -64,13 +64,6 @@ def swiftlint_repos(bzlmod = False): url = "https://github.com/krzyzanowskim/CryptoSwift/archive/refs/tags/1.8.4.tar.gz", ) - http_archive( - name = "com_github_ileitch_swift-filename-matcher", - sha256 = "1adbb1eb042910f996689827f7dee217bebf7c5178f34178bcfe468b5b3268a2", - strip_prefix = "swift-filename-matcher-2.0.0", - url = "https://github.com/ileitch/swift-filename-matcher/archive/refs/tags/2.0.0.tar.gz", - ) - def _swiftlint_repos_bzlmod(_): swiftlint_repos(bzlmod = True) From a9b49d90a9173c0fce3112efc71c35549b55399a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:22:57 +0000 Subject: [PATCH 215/260] Prepare 0.58.1 release --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Source/SwiftLintFramework/Models/Version.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e245749a73..9fb1838b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Main +## 0.58.1: New Year’s Fresh Fold #### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index aee479cf91..2c6ab25ad8 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.58.0", + version = "0.58.1", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Source/SwiftLintFramework/Models/Version.swift b/Source/SwiftLintFramework/Models/Version.swift index d381b75090..adcecda47e 100644 --- a/Source/SwiftLintFramework/Models/Version.swift +++ b/Source/SwiftLintFramework/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable, Sendable { } /// The current SwiftLint version. - public static let current = Self(value: "0.58.0") + public static let current = Self(value: "0.58.1") /// Public initializer. /// From 96fa57d0201c50cd578ce477a1e55713b977155b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:36:17 +0000 Subject: [PATCH 216/260] Release 0.58.1 --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 5f66e74579..148dde7024 100644 --- a/Package.swift +++ b/Package.swift @@ -195,8 +195,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.58.0/SwiftLintBinary.artifactbundle.zip", - checksum: "a9a9b4f94c4e0c65eda72c03c7e10d8105b0a50cbde18d375dab9b6e4fd9c052" + url: "https://github.com/realm/SwiftLint/releases/download/0.58.1/SwiftLintBinary.artifactbundle.zip", + checksum: "ce4c20a4107cb13ebdc25bad91307c78451df4df957a0727c249122587984ff5" ) ) #endif From 27bb35ed57892308f0b3969799773abea2ad88ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:36:24 +0000 Subject: [PATCH 217/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fb1838b4f..bb5bad249f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## Main + +#### Breaking + +* None. + +#### Experimental + +* None. + +#### Enhancements + +* None. + +#### Bug Fixes + +* None. + ## 0.58.1: New Year’s Fresh Fold #### Breaking From b67ad022c0bfd9018ee16663407e4cb49c4900ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 15 Jan 2025 19:59:11 +0100 Subject: [PATCH 218/260] Make Homebrew available in PATH before running action --- .github/workflows/post-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 4648396fdf..af33532f81 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -54,6 +54,8 @@ jobs: run: | AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + - name: Prepare Homebrew + run: eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - name: Update Homebrew formula uses: Homebrew/actions/bump-packages@master with: From bfefc384dbc810a717068dc92e62b3a17500abb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 10:00:06 +0100 Subject: [PATCH 219/260] Pull branch before Docker build --- .github/workflows/docker.yml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index dfb18b4098..930491f762 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -14,25 +14,28 @@ on: description: 'Docker tag' required: true type: string - default: 'latest' jobs: build: name: Build Docker Image runs-on: ubuntu-24.04 steps: + - name: Define variables on push to `main` + if: github.event_name == 'push' + run: | + echo "CHECKOUT_REF=main" >> $GITHUB_ENV + echo "DOCKER_TAG=latest" >> $GITHUB_ENV + - name: Define variables on workflow call + if: github.event_name != 'push' + run: | + echo "CHECKOUT_REF=${{ inputs.ref }}" >> $GITHUB_ENV + echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - uses: actions/checkout@v4 - if: github.event_name != 'push' && inputs.tag != 'latest' with: - ref: ${{ inputs.ref }} - - uses: actions/checkout@v4 - if: github.event_name == 'push' - - name: Set Docker tag - if: github.event_name != 'push' && inputs.tag != 'latest' - run: echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - - name: Use default Docker tag - if: github.event_name == 'push' - run: echo "DOCKER_TAG=latest" >> $GITHUB_ENV + ref: ${{ env.CHECKOUT_REF }} + - name: Update branch + if: github.event_name != 'push' + run: git pull - name: Set lowercase repository name run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> $GITHUB_ENV env: From 99c1dbe2c565f7d709058fdae4b6f34781624f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 22:11:39 +0100 Subject: [PATCH 220/260] Fix all linting issues in changelog --- CHANGELOG.md | 832 +++++++++++++++-------------- tools/add-new-changelog-section.sh | 23 +- tools/create-github-release.sh | 2 +- 3 files changed, 430 insertions(+), 427 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5bad249f..404171fc93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,26 @@ +# Changelog + ## Main -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * None. ## 0.58.1: New Year’s Fresh Fold -#### Breaking +### Breaking * If you are referring to the `swiftlint` binary from an Artifact Bundle consumed via Swift Package Manager in an Xcode Run Script Build Phase, make sure to update the path from @@ -43,15 +45,15 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5953](https://github.com/realm/SwiftLint/issues/5953) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix `redundant_sendable` correction by removing a remaining trailing comma as well when `Sendable` was last. [SimplyDanny](https://github.com/SimplyDanny) @@ -67,7 +69,7 @@ ## 0.58.0: New Year’s Fresh Fold -#### Breaking +### Breaking * The command plugin now requires write permissions so that it works with the `--fix` option without an error. [SimplyDanny](https://github.com/SimplyDanny) @@ -87,11 +89,11 @@ [Martin Redington](https://github.com/mildm8nnered) [#5912](https://github.com/realm/SwiftLint/issues/5912) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add Xcode command plugin allowing to run SwiftLint from within Xcode. [SimplyDanny](https://github.com/SimplyDanny) @@ -123,7 +125,7 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5018](https://github.com/realm/SwiftLint/issues/5018) -#### Bug Fixes +### Bug Fixes * Ignore TipKit's `#Rule` macro in `empty_count` rule. [Ueeek](https://github.com/Ueeek) @@ -140,15 +142,15 @@ ## 0.57.1: Squeaky Clean Cycle -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Suggest failable `String(bytes:encoding:)` initializer in `optional_data_string_conversion` rule as it accepts all `Sequence` @@ -193,7 +195,7 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5793](https://github.com/realm/SwiftLint/issues/5793) -#### Bug Fixes +### Bug Fixes * Run command plugin in whole package if no targets are defined in the package manifest. @@ -263,7 +265,7 @@ ## 0.57.0: Squeaky Clean Cycle -#### Breaking +### Breaking * The deprecated `anyobject_protocol` rule has now been removed. [Martin Redington](https://github.com/mildm8nnered) @@ -277,11 +279,11 @@ [Sam Rayner](https://github.com/samrayner) [#5263](https://github.com/realm/SwiftLint/issues/5263) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `ignore_multiline_type_headers` and `ignore_multiline_statement_conditions` options to `opening_brace` rule to allow opening braces to be on a new line after @@ -300,7 +302,7 @@ [Martin Redington](https://github.com/mildm8nnered) [#5778](https://github.com/realm/SwiftLint/issues/5778) -#### Bug Fixes +### Bug Fixes * `superfluous_disable_command` violations are now triggered for custom rules. @@ -316,19 +318,19 @@ ## 0.56.2: Heat Pump Dryer -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Ignore initializers with attributes in `unneeded_synthesized_initializer` rule. [SimplyDanny](https://github.com/SimplyDanny) @@ -361,26 +363,26 @@ ## 0.56.1: Heat Pump Dryer -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Let `contrasted_opening_brace` be an opt-in rule. [SimplyDanny](https://github.com/SimplyDanny) ## 0.56.0: Heat Pump Dryer -#### Breaking +### Breaking * The deprecated `--path` and `--in-process-sourcekit` arguments have now been removed completely. @@ -397,11 +399,11 @@ reports about wrong correction positions so far. [SimplyDanny](https://github.com/SimplyDanny) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add new `attribute_name_spacing` rule to enforce no trailing whitespace between attribute names and parentheses, ensuring compatibility with Swift 6, where this spacing @@ -471,7 +473,7 @@ [Martin Redington](https://github.com/mildm8nnered) [#5666](https://github.com/realm/SwiftLint/issues/5666) -#### Bug Fixes +### Bug Fixes * Fix a few false positives and negatives by updating the parser to support Swift 6 with all its new language constructs. @@ -533,21 +535,21 @@ ## 0.55.1: Universal Washing Powder -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Clarify wording of `static_over_final_class` rule's violation message. [SimplyDanny](https://github.com/SimplyDanny) [#5570](https://github.com/realm/SwiftLint/issues/5570) -#### Bug Fixes +### Bug Fixes * Fix Bazel build when `bzlmod` is not in use by adding transitive dependencies explicitly. @@ -577,7 +579,7 @@ ## 0.55.0: Universal Washing Powder -#### Breaking +### Breaking * Rewrite `SwiftLintBuildToolPlugin` using `BUILD_WORKSPACE_DIRECTORY` without relying on the `--config` option. @@ -604,7 +606,7 @@ preserve the previous behavior. [Garric Nahapetian](https://github.com/garricn) -#### Experimental +### Experimental * Add two new options to the `lint` and `analyze` commands: `--write-baseline` to save a baseline to disk, and `--baseline` to read a saved baseline and @@ -614,7 +616,7 @@ [#5475](https://github.com/realm/SwiftLint/pull/5475) [#3421](https://github.com/realm/SwiftLint/pull/3421) -#### Enhancements +### Enhancements * Add a reporter that outputs violations in the Static Analysis Results Interchange Format (SARIF). @@ -784,7 +786,7 @@ [mildm8nnered](https://github.com/mildm8nnered) [#5295](https://github.com/realm/SwiftLint/issues/5295) -#### Bug Fixes +### Bug Fixes * Invalid keys in a configuration don't lead to the default configuration being used anymore. The invalid key will just be reported but otherwise ignored. @@ -882,17 +884,17 @@ ## 0.54.0: Macro-Economic Forces -#### Breaking +### Breaking * SwiftLint now requires Swift 5.9 or higher to build. [SimplyDanny](https://github.com/SimplyDanny) [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `only` configuration option to `todo` rule which allows to specify whether the rule shall trigger on `TODO`s, `FIXME`s or both. @@ -934,7 +936,7 @@ * Rewrite `no_grouping_extension` rule using SwiftSyntax. [Marcelo Fabri](https://github.com/marcelofabri) -#### Bug Fixes +### Bug Fixes * Fix false positive in `implicit_getter` rule when using unknown accessors. [kabiroberai](https://github.com/kabiroberai) @@ -961,7 +963,7 @@ ## 0.53.0: Laundry List -#### Breaking +### Breaking * Hide all `Reporter`s from SwiftLint's' public interface. [SimplyDanny](https://github.com/SimplyDanny) @@ -976,11 +978,11 @@ [Martin Redington](https://github.com/mildm8nnered) [#4798](https://github.com/realm/SwiftLint/issues/4798) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Show specific violation message for the `attributes` rule when the option `always_on_line_above` or `attributes_with_arguments_always_on_line_above` @@ -1053,7 +1055,7 @@ `weak_except_iboutlets` that only checks `weak` variables. [Ricky Tan](https://github.com/rickytan) -#### Bug Fixes +### Bug Fixes * Respect grapheme clusters in counting the number of characters in the `collection_alignment` rule. [kishikawakatsumi](https://github.com/kishikawakatsumi) @@ -1140,15 +1142,15 @@ ## 0.52.4: Lid Switch -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Handle static `spec` methods in `quick_discouraged_call` rule. The method type changed from an instance method to a class method in Quick 7. @@ -1159,7 +1161,7 @@ on the website. [SimplyDanny](https://github.com/SimplyDanny) -#### Bug Fixes +### Bug Fixes * Fix false positives for the `unneeded_synthesized_initializer` rule, when no argument initializers had side-effects. @@ -1186,15 +1188,15 @@ ## 0.52.3: Duplicate Hampers -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Make severity for unallowed symbols configurable. The option name is `unallowed_symbols_severity`. It accepts the two values `warning` and `error` @@ -1235,7 +1237,7 @@ automatically synthesized. [Martin Redington](https://github.com/mildm8nnered) -#### Bug Fixes +### Bug Fixes * The option `validates_start_with_lowercase` can now be disabled by setting it to `off`. @@ -1275,22 +1277,22 @@ ## 0.52.2: Crisper Clearer Pleats -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Exclude simple assignments of the form `self.x = x` from being reported by the `redundant_self_in_closure` rule. [SimplyDanny](https://github.com/SimplyDanny) [#4988](https://github.com/realm/SwiftLint/issues/4988) -#### Bug Fixes +### Bug Fixes * Make `unhandled_throwing_task` opt-in instead of enabled by default. The rule is still prone to false positives at this point, so this makes enabling the @@ -1305,19 +1307,19 @@ ## 0.52.1: Crisp Clear Pleats -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Let the `validates_start_with_lowercase` option in name configurations expect a severity (warning or error). Not setting it disables the check. @@ -1336,7 +1338,7 @@ ## 0.52.0: Crisp Clear Pleats -#### Breaking +### Breaking * The `attributes` rule now expects attributes with arguments to be placed on their own line above the declaration they are supposed to influence. @@ -1353,11 +1355,11 @@ `SwiftLintExtraRules` to add your own native rules to SwiftLint. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add new `superfluous_else` rule that triggers on `if`-statements when an attached `else`-block can be removed, because all branches of the previous @@ -1422,7 +1424,7 @@ See [this forum thread](https://forums.swift.org/t/56066) for more details. [kylebshr](https://github.com/kylebshr) -#### Bug Fixes +### Bug Fixes * Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace. [SimplyDanny](https://github.com/SimplyDanny) @@ -1451,7 +1453,7 @@ ## 0.51.0: bzllint -#### Breaking +### Breaking * Deprecate the `unused_capture_list` rule in favor of the Swift compiler warning. At the same time, make it an opt-in rule. @@ -1470,11 +1472,11 @@ [Moly](https://github.com/kyounh12) [#4655](https://github.com/realm/SwiftLint/pull/4655) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `duplicate_conditions` rule which warns when a condition is duplicated in separate branches of the same branching statement (if-else, or switch). @@ -1568,7 +1570,7 @@ of each rule in a text table. [Martin Redington](https://github.com/mildm8nnered) -#### Bug Fixes +### Bug Fixes * Report violations in all `_length` rules when the error threshold is smaller than the warning threshold. @@ -1646,15 +1648,15 @@ ## 0.50.3: Bundle of Towels -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * The `SwiftLintPlugin` SwiftPM plugin now uses a prebuilt binary on macOS. @@ -1683,7 +1685,7 @@ [SimplyDanny](https://github.com/SimplyDanny) [#4612](https://github.com/realm/SwiftLint/issues/4612) -#### Bug Fixes +### Bug Fixes * Fix configuration parsing error in `unused_declaration` rule. [SimplyDanny](https://github.com/SimplyDanny) @@ -1705,15 +1707,15 @@ ## 0.50.1: Artisanal Clothes Pegs Fixup Edition -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Moved the validation of doc comments in local scopes out of `orphaned_doc_comment` and into a new opt-in `local_doc_comment` rule. @@ -1725,7 +1727,7 @@ [Tony Arnold](https://github.com/tonyarnold) [#4406](https://github.com/realm/SwiftLint/pull/4406) -#### Bug Fixes +### Bug Fixes * Fix building with `swift build -c release`. [JP Simard](https://github.com/jpsim) @@ -1762,7 +1764,7 @@ ## 0.50.0: Artisanal Clothes Pegs -#### Breaking +### Breaking * SwiftLint now requires Swift 5.7 or higher to build. [JP Simard](https://github.com/jpsim) @@ -1781,11 +1783,11 @@ SwiftLintFramework module. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * SwiftSyntax libraries have been updated from the previous 5.6 release and now use the new parser written in Swift. @@ -2077,7 +2079,7 @@ [benjamin-kramer](https://github.com/benjamin-kramer) [#3940](https://github.com/realm/SwiftLint/issues/3940) -#### Bug Fixes +### Bug Fixes * Respect `validates_start_with_lowercase` option when linting function names. [Chris Brakebill](https://github.com/braker1nine) @@ -2130,15 +2132,15 @@ _Note: The default branch for the SwiftLint git repository was renamed from `master` to `main` on September 1st. Please update any code or automation accordingly._ -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add new `self_binding` opt-in rule to enforce that `self` identifiers are consistently re-bound to a common identifier name. Configure `bind_identifier` @@ -2160,7 +2162,7 @@ accordingly._ [SimplyDanny](https://github.com/SimplyDanny) [#4127](https://github.com/realm/SwiftLint/issues/4127) -#### Bug Fixes +### Bug Fixes * Migrate `empty_xctest_method` rule to SwiftSyntax fixing some false positives. [SimplyDanny](https://github.com/SimplyDanny) @@ -2182,7 +2184,7 @@ _Note: The default branch for the SwiftLint git repository will be renamed from `master` to `main` on September 1st. Please update any code or automation accordingly._ -#### Breaking +### Breaking * SwiftLint now requires Swift 5.6 or higher to build, and macOS 12 or higher to run. @@ -2208,11 +2210,11 @@ accordingly._ uses an in-process SourceKit. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Make `duplicate_imports` rule correctable. Fix `duplicate_imports` rule reporting redundant violations when more than one duplicate is present. @@ -2279,7 +2281,7 @@ accordingly._ SwiftSyntax-based rules. [JP Simard](https://github.com/jpsim) -#### Bug Fixes +### Bug Fixes * Fix false positive in `self_in_property_initialization` rule when using closures inside `didSet` and other accessors. @@ -2307,17 +2309,17 @@ accordingly._ This is the last release to support building with Swift 5.5.x and running on macOS < 12. -#### Breaking +### Breaking * Deprecate the `--path` options for `lint`/`analyze` commands. Prefer the positional paths that can be added last to both commands. [SimplyDanny](https://github.com/SimplyDanny) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Support `iOSApplicationExtension`, `macOSApplicationExtension`, `watchOSApplicationExtension`, and `tvOSApplicationExtension` identifiers @@ -2351,7 +2353,7 @@ macOS < 12. [KokiHirokawa](https://github.com/KokiHirokawa) [#3986](https://github.com/realm/SwiftLint/issues/3986) -#### Bug Fixes +### Bug Fixes * Ignore array types in `syntactic_sugar` rule if their associated `Index` is accessed. @@ -2398,15 +2400,15 @@ macOS < 12. ## 0.47.1: Smarter Appliance -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add type-checked analyzer rule version of `ArrayInitRule` named `TypesafeArrayInitRule` with identifier `typesafe_array_init` that @@ -2449,7 +2451,7 @@ macOS < 12. [Marcelo Fabri](https://github.com/marcelofabri) [#3950](https://github.com/realm/SwiftLint/issues/3950) -#### Bug Fixes +### Bug Fixes * Fix false positives in `unused_closure_parameter` when using parameters with backticks. @@ -2476,7 +2478,7 @@ macOS < 12. ## 0.47.0: Smart Appliance -#### Breaking +### Breaking * SwiftLint now requires Swift 5.5 or higher to build. [JP Simard](https://github.com/jpsim) @@ -2490,7 +2492,7 @@ macOS < 12. * SwiftLint now requires at least Swift 5.0 installed in order to lint files. [Marcelo Fabri](https://github.com/marcelofabri) -#### Experimental +### Experimental * The `force_cast` rule and the comment command parsing mechanism have been updated to use SwiftSyntax instead of SourceKit. Please report any problems @@ -2498,7 +2500,7 @@ macOS < 12. use Swift Syntax in the future. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Empty files no longer trigger any violations. [JP Simard](https://github.com/jpsim) @@ -2528,7 +2530,7 @@ macOS < 12. [PaulTaykalo](https://github.com/PaulTaykalo) [#3911](https://github.com/realm/SwiftLint/issues/3911) -#### Bug Fixes +### Bug Fixes * Extend `class_delegate_protocol` to correctly identify cases with the protocol body opening brace on a new line. @@ -2541,19 +2543,19 @@ macOS < 12. ## 0.46.5: Laundry Studio -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix `empty_parentheses_with_trailing_closure` rule when using Swift 5.6. [Marcelo Fabri](https://github.com/marcelofabri) @@ -2577,19 +2579,19 @@ macOS < 12. ## 0.46.4: Detergent Tray -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Ignore meta class types in `prefer_self_in_static_references` rule. [SimplyDanny](https://github.com/SimplyDanny) @@ -2607,21 +2609,21 @@ macOS < 12. ## 0.46.3: Detergent Spill -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Change fingerprint generation in `CodeClimateReporter.swift` to use the relative file path to better support CI/CD on multiple machines. [HA Pors](https://github.com/hpors) -#### Bug Fixes +### Bug Fixes * Fix crash in the `closure_end_indentation` rule when linting with Swift 5.6. @@ -2634,19 +2636,19 @@ macOS < 12. ## 0.46.2: Detergent Package -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix SwiftLint.pkg installer on macOS 11 or later. [JP Simard](https://github.com/jpsim) @@ -2663,18 +2665,18 @@ macOS < 12. ## 0.46.1: Detergent Container -#### Breaking +### Breaking * The `weak_delegate` rule has been opt-in due to its high false positive rate. [JP Simard](https://github.com/jpsim) [#2786](https://github.com/realm/SwiftLint/issues/2786) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Official Docker images are now available. See the [Docker section of the README](README.md#docker) for usage @@ -2685,7 +2687,7 @@ macOS < 12. [Adrian Debbeler](https://github.com/grosem) [#2585](https://github.com/realm/SwiftLint/issues/2585) -#### Bug Fixes +### Bug Fixes * Fix `convenience_type` false positives when using actors. [JP Simard](https://github.com/jpsim) @@ -2715,15 +2717,15 @@ macOS < 12. ## 0.45.1: Clothes Drying Hooks -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Update Rule list documentation to distinguish between opt-in and on-by-default rules. @@ -2739,7 +2741,7 @@ macOS < 12. [Jesse Crocker](https://github.com/JesseCrocker) [Hannes Ljungberg](https://github.com/hannseman) -#### Bug Fixes +### Bug Fixes * Fix `unused_import` rule incorrectly considering `SwiftShims` as a used import. @@ -2756,16 +2758,16 @@ macOS < 12. ## 0.45.0: Effectful Apparel -#### Breaking +### Breaking * SwiftLint now requires Swift 5.4 or higher to build. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `self_in_property_initialization` rule to catch uses of `self` inside an inline closure used for initializing a variable. In this case, @@ -2792,7 +2794,7 @@ macOS < 12. [PaulTaykalo](https://github.com/PaulTaykalo) [#3747](https://github.com/realm/SwiftLint/issues/3747) -#### Bug Fixes +### Bug Fixes * Fix a bug with the `missing_docs` rule where `excludes_inherited_types` would not be set. @@ -2818,16 +2820,16 @@ macOS < 12. ## 0.44.0: Travel Size Lint Roller -#### Breaking +### Breaking * SwiftLint now requires Swift 5.3 or higher to build. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add configuration options to `missing_docs` rule: * `excludes_extensions` defaults to `true` to skip reporting violations @@ -2870,7 +2872,7 @@ macOS < 12. named arguments are used in closures that span multiple lines. [Marcelo Fabri](https://github.com/marcelofabri) -#### Bug Fixes +### Bug Fixes * Fix false positives in `empty_enum_arguments` rule when comparing values with a static member (e.g. `if number == .zero`). @@ -2900,19 +2902,19 @@ macOS < 12. ## 0.43.1: Laundroformat -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix the File Length rule name. [onato](https://github.com/onato) @@ -2925,7 +2927,7 @@ macOS < 12. ## 0.43.0: Clothes Line Interface -#### Breaking +### Breaking * The command line syntax has slightly changed due to migrating from the Commandant command line parsing library to swift-argument-parser. @@ -2953,11 +2955,11 @@ macOS < 12. [#3498](https://github.com/realm/SwiftLint/issues/3498). [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Added `allows_single_line` option in `multiline_parameters` rule configuration. Defaults to `true`. This enforces parameters in a method @@ -3027,7 +3029,7 @@ macOS < 12. public Combine subjects. [Otavio Cordeiro](https://github.com/otaviocc) -#### Bug Fixes +### Bug Fixes * Fix `custom_rules` merging when the parent configuration is based on `only_rules`. @@ -3069,7 +3071,7 @@ macOS < 12. ## 0.42.0: He Chutes, He Scores -#### Breaking +### Breaking * SwiftLint now requires Swift 5.2 or higher to build. [JP Simard](https://github.com/jpsim) @@ -3108,11 +3110,11 @@ macOS < 12. [Bryan Ricker](https://github.com/bricker) [#3426](https://github.com/realm/SwiftLint/pull/3426) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Don't report `@UIApplicationDelegateAdaptor` statements in `weak-delegate` rule. [Richard Turton](https://github.com/jrturton) @@ -3154,7 +3156,7 @@ macOS < 12. [Dalton Claybrook](https://github.com/daltonclaybrook) [#3415](https://github.com/realm/SwiftLint/issues/3415) -#### Bug Fixes +### Bug Fixes * Remove `@IBOutlet` and `@IBInspectable` from UnusedDeclarationRule. [Keith Smiley](https://github.com/keith) @@ -3162,7 +3164,7 @@ macOS < 12. ## 0.41.0: World’s Cleanest Voting Booth -#### Breaking +### Breaking * Changed behavior of `strict` option on `lint` and `analyze` to treat all warnings as errors instead of only changing the exit code. @@ -3178,11 +3180,11 @@ macOS < 12. `only_rules`. [Dalton Claybrook](https://github.com/daltonclaybrook) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `use-alternative-excluding` option to speed up linting in cases described in [#3325](https://github.com/realm/SwiftLint/pull/3325). @@ -3237,7 +3239,7 @@ macOS < 12. [Paul Taykalo](https://github.com/PaulTaykalo) [#3388](https://github.com/realm/SwiftLint/issues/3388) -#### Bug Fixes +### Bug Fixes * Fix parsing of Xcode 12 compiler logs for analyzer rules. [JP Simard](https://github.com/jpsim) @@ -3305,15 +3307,15 @@ macOS < 12. ## 0.40.3: Greased Up Drum Bearings -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Make the `unused_declaration` rule run 3-5 times faster, and enable it to detect more occurrences of unused declarations. @@ -3328,7 +3330,7 @@ macOS < 12. [JP Simard](https://github.com/jpsim) [#3343](https://github.com/realm/SwiftLint/issues/3343) -#### Bug Fixes +### Bug Fixes * Rule `unused_capture_list` should not be triggered by self keyword. [hank121314](https://github.com/hank121314) @@ -3341,15 +3343,15 @@ macOS < 12. ## 0.40.2: Demo Unit -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Improve description for `empty_enum_arguments`. [Lukas Schmidt](https://github.com/lightsprint09) @@ -3357,26 +3359,26 @@ macOS < 12. * Add support for `excluded_match_kinds` custom rule config parameter. [Ryan Demo](https://github.com/ryandemo) -#### Bug Fixes +### Bug Fixes * None. ## 0.40.1: A Baffling Response -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add support for params files for file paths. [keith](https://github.com/keith) -#### Bug Fixes +### Bug Fixes * Fix .swift-version to use Swift 5.1. [cfiken](https://github.com/cfiken) @@ -3388,7 +3390,7 @@ macOS < 12. ## 0.40.0: Washable Mask -#### Breaking +### Breaking * SwiftLint now requires Swift 5.1 or higher to build. [JP Simard](https://github.com/jpsim) @@ -3397,11 +3399,11 @@ macOS < 12. provided invalid files or arguments, the command will now abort. [Keith Smiley](https://github.com/keith) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * JUnit reporter for GitLab artifact:report:junit with better representation of found issues. @@ -3436,7 +3438,7 @@ macOS < 12. [Amzd](https://github.com/Amzd) [#2755](https://github.com/realm/SwiftLint/issues/2755) -#### Bug Fixes +### Bug Fixes * Fix UnusedImportRule breaking transitive imports. [keith](https://github.com/keith) @@ -3496,15 +3498,15 @@ macOS < 12. This is the last release to support building with Swift 5.0.x. -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add configuration options to the `unused_import` rule to require explicit import statements for each module referenced in a source @@ -3515,7 +3517,7 @@ This is the last release to support building with Swift 5.0.x. [JP Simard](https://github.com/jpsim) [#3116](https://github.com/realm/SwiftLint/issues/3116) -#### Bug Fixes +### Bug Fixes * Fix more false positives in `implicit_getter` rule in extensions when using Swift 5.2. @@ -3536,7 +3538,7 @@ This is the last release to support building with Swift 5.0.x. ## 0.39.1: The Laundromat has a Rotating Door -#### Breaking +### Breaking * The new rules introduced in 0.39.0 that depend on SwiftSyntax have been temporarily removed as we work out release packaging issues. @@ -3548,15 +3550,15 @@ This is the last release to support building with Swift 5.0.x. [JP Simard](https://github.com/jpsim) [#3105](https://github.com/realm/SwiftLint/issues/3105) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix unused_import rule reported locations and corrections when multiple `@testable` imports are involved. @@ -3564,7 +3566,7 @@ This is the last release to support building with Swift 5.0.x. ## 0.39.0: A Visitor in the Laundromat -#### Breaking +### Breaking * Replace all uses of `Int`/`Int64`/`NSRange` representing byte offsets to use newly introduced `ByteCount` and `ByteRange` values instead. @@ -3577,11 +3579,11 @@ This is the last release to support building with Swift 5.0.x. and requires Xcode 11.0 to build. [Marcelo Fabri](https://github.com/marcelofabri) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add option to pass successfully if no files passed to SwiftLint are lintable. [thedavidharris](https://github.com/thedavidharris) @@ -3633,7 +3635,7 @@ This is the last release to support building with Swift 5.0.x. [Cihat Gündüz](https://github.com/Jeehut) [#2860](https://github.com/realm/SwiftLint/issues/2860) -#### Bug Fixes +### Bug Fixes * Fix false positive in `attributes` rule with `@autoclosure` parameters when using Swift 5.2. @@ -3676,15 +3678,15 @@ This is the last release to support building with Swift 5.0.x. ## 0.38.2: Machine Repair Manual -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add option to configure which kinds of expressions should omit their `return` keyword by introducing an `included` configuration for the @@ -3729,7 +3731,7 @@ This is the last release to support building with Swift 5.0.x. [#2933](https://github.com/realm/SwiftLint/issues/2933) [#2961](https://github.com/realm/SwiftLint/issues/2961) -#### Bug Fixes +### Bug Fixes * Fix issues in `unused_import` rule when correcting violations in files containing `@testable` imports where more than the unused imports would be @@ -3738,15 +3740,15 @@ This is the last release to support building with Swift 5.0.x. ## 0.38.1: Extra Shiny Pulsator Cap -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Make `weak_delegate` rule correctable. [MaxHaertwig](https://github.com/maxhaertwig) @@ -3770,7 +3772,7 @@ This is the last release to support building with Swift 5.0.x. [Marcelo Fabri](https://github.com/marcelofabri) [#3003](https://github.com/realm/SwiftLint/issues/3003) -#### Bug Fixes +### Bug Fixes * Fix crash in `unused_import` rule when unused imports have trailing comments. @@ -3797,7 +3799,7 @@ This is the last release to support building with Swift 5.0.x. ## 0.38.0: Toroidal Agitation -#### Breaking +### Breaking * Replace the `SyntaxToken` and `SyntaxMap` structures used in public SwiftLintFramework APIs with a new `SwiftLintSyntaxToken` @@ -3806,11 +3808,11 @@ This is the last release to support building with Swift 5.0.x. [PaulTaykalo](https://github.com/PaulTaykalo) [#2955](https://github.com/realm/SwiftLint/issues/2955) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Make `control_statement` rule correctable. [MaxHaertwig](https://github.com/maxhaertwig) @@ -3824,7 +3826,7 @@ This is the last release to support building with Swift 5.0.x. [kastiglione](https://github.com/kastiglione) [#2962](https://github.com/realm/SwiftLint/issues/2962) -#### Bug Fixes +### Bug Fixes * Fix false positive for LetVarWhitespaceRule. [PaulTaykalo](https://github.com/PaulTaykalo) @@ -3840,7 +3842,7 @@ This is the last release to support building with Swift 5.0.x. ## 0.37.0: Double Load -#### Breaking +### Breaking * Replace the `[String: SourceKittenRepresentable]` dictionaries used in public SwiftLintFramework APIs with a new `SourceKittenDictionary` @@ -3853,11 +3855,11 @@ This is the last release to support building with Swift 5.0.x. library's `Result` type. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Speed up many operations by using SwiftLintFile wrapper over File from SourceKitten, caching most members derived from the File. @@ -3892,22 +3894,22 @@ This is the last release to support building with Swift 5.0.x. * Add GitHub Actions Logging reporter (`github-actions-logging`). [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * None. ## 0.36.0: 👕👚👗 -#### Breaking +### Breaking * SwiftLint now requires Swift 5.0 or higher to build. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `contains_over_range_nil_comparison` opt-in rule to prefer using `contains` over comparison of `range(of:)` to `nil`. @@ -3948,7 +3950,7 @@ This is the last release to support building with Swift 5.0.x. [PaulTaykalo](https://github.com/PaulTaykalo) [#2901](https://github.com/realm/SwiftLint/issues/2901) -#### Bug Fixes +### Bug Fixes * Fix running analyzer rules on the output of builds performed with Xcode 11 or later. @@ -3958,15 +3960,15 @@ This is the last release to support building with Swift 5.0.x. This is the last release to support building with Swift 4.2.x. -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Type name rules considers SwiftUI template code. [atfelix](https://github.com/atfelix) @@ -3990,7 +3992,7 @@ This is the last release to support building with Swift 4.2.x. [Colton Schlosser](https://github.com/cltnschlosser) [#2807](https://github.com/realm/SwiftLint/issues/2807) -#### Bug Fixes +### Bug Fixes * Fixed false positive in `colon` rule inside guard and ternary operator. [Andrey Uryadov](https://github.com/a-25) @@ -4024,7 +4026,7 @@ This is the last release to support building with Swift 4.2.x. ## 0.34.0: Anti-Static Wool Dryer Balls -#### Breaking +### Breaking * To enable collecting rules, many breaking changes to `SwiftLintFramework`'s public API were made the `Linter` type was significantely changed, and a new @@ -4038,7 +4040,7 @@ This is the last release to support building with Swift 4.2.x. [Elliott Williams](https://github.com/elliottwilliams) [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * Add a two-stage `CollectingRule` protocol to support rules that collect data from all files before validating. Collecting rules implement a `collect` @@ -4058,27 +4060,27 @@ This is the last release to support building with Swift 4.2.x. `unused_declaration` instead. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Added 'file_name_no_space' opt-in rule. [timcmiller](https://github.com/timcmiller) [#3007](https://github.com/realm/SwiftLint/issues/3007) -#### Bug Fixes +### Bug Fixes * None. ## 0.33.1: Coin-Operated Property Wrapper -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Significantly improve performance when running with a large number of cached configurations or when running with many cached results. @@ -4096,7 +4098,7 @@ This is the last release to support building with Swift 4.2.x. of another path being linted. [Keith Smiley](https://github.com/keith) -#### Bug Fixes +### Bug Fixes * Don't trigger `vertical_parameter_alignment` violations when using parameters with attributes such as `@ViewBuilder` in function declarations. @@ -4110,18 +4112,18 @@ This is the last release to support building with Swift 4.2.x. ## 0.33.0: Worldwide Dryers Conference -#### Breaking +### Breaking * Remove the `weak_computed_property` rule. Please see linked issue for discussion and rationale. [JP Simard](https://github.com/jpsim) [#2712](https://github.com/realm/SwiftLint/issues/2712) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `" - "` delimiter to allow commenting SwiftLint commands without triggering `superfluous_disable_command`. @@ -4164,7 +4166,7 @@ This is the last release to support building with Swift 4.2.x. [Marcelo Fabri](https://github.com/marcelofabri) [#2612](https://github.com/realm/SwiftLint/issues/2612) -#### Bug Fixes +### Bug Fixes * Don't trigger `redundant_void_return` violations when using `subscript` as the return type is required. @@ -4186,15 +4188,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.32.0: Wash-N-Fold-N-Reduce -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `reduce_boolean` rule to prefer simpler constructs over `reduce(Boolean)`. [Xavier Lowmiller](https://github.com/xavierLowmiller) @@ -4244,7 +4246,7 @@ This is the last release to support building with Swift 4.2.x. [Norio Nomura](https://github.com/norio-nomura) [#2693](https://github.com/realm/SwiftLint/issues/2693) -#### Bug Fixes +### Bug Fixes * Fix bug where SwiftLint ignores excluded files list in a nested configuration file. @@ -4293,15 +4295,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.31.0: Busy Laundromat -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `deployment_target` rule to validate that `@availability` attributes and `#available` conditions are not using a version that is satisfied by the @@ -4343,7 +4345,7 @@ This is the last release to support building with Swift 4.2.x. [Cihat Gündüz](https://github.com/Dschee) [#2637](https://github.com/realm/SwiftLint/issues/2637) -#### Bug Fixes +### Bug Fixes * Fix false positives on `no_grouping_extension` rule when using `where` clause. @@ -4381,25 +4383,25 @@ This is the last release to support building with Swift 4.2.x. ## 0.30.1: Localized Stain Remover -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * Silence `CodingKeys` violations in `unused_private_declaration` since these should always be intentional violations. [Kim de Vos](https://github.com/kimdv) [#2573](https://github.com/realm/SwiftLint/issues/2573) -#### Enhancements +### Enhancements * Add `nslocalizedstring_key` opt-in rule to validate that keys used in `NSLocalizedString` calls are static strings, so `genstrings` will be able to find them. [Marcelo Fabri](https://github.com/marcelofabri) -#### Bug Fixes +### Bug Fixes * Fix false positives on `trailing_closure` rule when using anonymous closure calls. @@ -4417,15 +4419,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.30.0: A New Washer and Dryer Set -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `duplicate_imports` rule to prevent importing the same module twice. [Samuel Susla](https://github.com/sammy-sc) @@ -4441,7 +4443,7 @@ This is the last release to support building with Swift 4.2.x. muted parameter. [Marcelo Fabri](https://github.com/marcelofabri) -#### Bug Fixes +### Bug Fixes * Fix false positives on `identical_operands` rule when the right side of the operand has a chained optional. @@ -4450,25 +4452,25 @@ This is the last release to support building with Swift 4.2.x. ## 0.29.4: In-Unit Operands -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * Fix `unused_import` correction deleting unrelated ranges when there are multiple violations in a single file. [JP Simard](https://github.com/jpsim) [#2561](https://github.com/realm/SwiftLint/issues/2561) -#### Enhancements +### Enhancements * Add `strong_iboutlet` opt-in rule to enforce that `@IBOutlet`s are not declared as `weak`. [Marcelo Fabri](https://github.com/marcelofabri) [#2433](https://github.com/realm/SwiftLint/issues/2433) -#### Bug Fixes +### Bug Fixes * Fix inaccessible custom rules in nested configurations. [Timofey Solonin](https://github.com/biboran) @@ -4487,17 +4489,17 @@ This is the last release to support building with Swift 4.2.x. ## 0.29.3: Entangled Agitator -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * Skip `@IBInspectable` and `deinit` declarations in `unused_private_declaration`. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Allow configuring `discouraged_object_literal` rule to only discourage one kind of object literal. @@ -4519,7 +4521,7 @@ This is the last release to support building with Swift 4.2.x. [Marcelo Fabri](https://github.com/marcelofabri) [#2227](https://github.com/realm/SwiftLint/issues/2227) -#### Bug Fixes +### Bug Fixes * Fix false positives on `first_where` rule when calling `filter` without a closure parameter (for example on a Realm collection). @@ -4542,15 +4544,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.29.2: Washateria -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add new opt-in rule `vertical_whitespace_opening_braces` to warn against empty lines after opening braces. @@ -4583,7 +4585,7 @@ This is the last release to support building with Swift 4.2.x. [Timofey Solonin](https://github.com/biboran) [#2353](https://github.com/realm/SwiftLint/issues/2353) -#### Bug Fixes +### Bug Fixes * Fix false positives in `redundant_objc_attribute` for private declarations under `@objcMembers`. @@ -4600,15 +4602,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.29.1: There’s Always More Laundry -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `redundant_objc_attribute` to warn against already implied `@objc` attribute. @@ -4644,7 +4646,7 @@ This is the last release to support building with Swift 4.2.x. [Kim de Vos](https://github.com/kimdv) [#2074](https://github.com/realm/SwiftLint/issues/2074) -#### Bug Fixes +### Bug Fixes * Fix false positive in `nimble_operator` rule. [Marcelo Fabri](https://github.com/marcelofabri) @@ -4669,16 +4671,16 @@ This is the last release to support building with Swift 4.2.x. ## 0.29.0: A Laundry List of Changes -#### Breaking +### Breaking * SwiftLint now requires Swift 4.2 or higher to build. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Improve the performance of saving or reading cached lint results on platforms with CommonCrypto. @@ -4692,7 +4694,7 @@ This is the last release to support building with Swift 4.2.x. `homebrew-core/Formula/swiftlint.rb` within sandbox. [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * Fix compiler warnings when building with Swift 4.2 introduced in the last release. @@ -4723,15 +4725,15 @@ This is the last release to support building with Swift 4.2.x. ## 0.28.2: EnviroBoost Plus -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * Add `SWIFTLINT_DISABLE_SOURCEKIT` environment variable to allow running SwiftLint without connecting to SourceKit. This will run a subset of rules @@ -4739,7 +4741,7 @@ This is the last release to support building with Swift 4.2.x. setting such as in Homebrew's CI. [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * None. @@ -4747,19 +4749,19 @@ This is the last release to support building with Swift 4.2.x. This is the last release to support building with Swift 4.0 and Swift 4.1. -#### Breaking +### Breaking * None. -#### Experimental +### Experimental * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Improve the performance of collecting which files to lint by up to 3.5x. [JP Simard](https://github.com/jpsim) @@ -4770,14 +4772,14 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. ## 0.28.0: EcoBoost -#### Breaking +### Breaking * Completely remove the `--use-tabs` option of the `autocorrect` command that was deprecated in 0.24.1. In its place, define an `indentation` key in your configuration files. [JP Simard](https://github.com/jpsim) -#### Experimental +### Experimental * Add a new `swiftlint analyze` command which can lint Swift files using the full type-checked AST. Rules of the `AnalyzerRule` type will be added over @@ -4800,7 +4802,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. declarations. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Add `legacy_random` opt-in rule to encourage the use of `.random(in:)` instead of `arc4random`, `arc4random_uniform`, and `drand48`. @@ -4854,7 +4856,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. [Timofey Solonin](https://github.com/biboran) [#2422](https://github.com/realm/SwiftLint/issues/2422) -#### Bug Fixes +### Bug Fixes * Fix `comma` rule false positives on object literals (for example, images). [Marcelo Fabri](https://github.com/marcelofabri) @@ -4882,11 +4884,11 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. ## 0.27.0: Heavy Duty -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Append `modifier_order` description with failure reason. [Daniel Metzing](https://github.com/dirtydanee) @@ -4938,7 +4940,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. [Andrés Cecilia Luque](https://github.com/acecilia) [#1652](https://github.com/realm/SwiftLint/issues/1652) -#### Bug Fixes +### Bug Fixes * Fix an issue with `control_statement` where commas in clauses prevented the rule from applying. @@ -4975,7 +4977,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. ## 0.26.0: Maytagged Pointers -#### Breaking +### Breaking * SwiftLint now requires Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) @@ -4984,7 +4986,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. [Marcelo Fabri](https://github.com/marcelofabri) [#1892](https://github.com/realm/SwiftLint/issues/1892) -#### Enhancements +### Enhancements * Add optional filename verification to the `file_header` rule. All occurrences in the pattern of the `SWIFTLINT_CURRENT_FILENAME` @@ -5082,7 +5084,7 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. [Ben Asher](https://github.com/benasher44) [#810](https://github.com/realm/SwiftLint/issues/810) -#### Bug Fixes +### Bug Fixes * Update `LowerACLThanParent` rule to not lint extensions. [Keith Smiley](https://github.com/keith) @@ -5133,11 +5135,11 @@ This is the last release to support building with Swift 4.0 and Swift 4.1. This is the last release to support building with Swift 3.2 and Swift 3.3. The next release will require Swift 4.0 or higher to build. -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Add `LowerACLThanParent` rule. [Keith Smiley](https://github.com/keith) @@ -5187,7 +5189,7 @@ The next release will require Swift 4.0 or higher to build. [Norio Nomura](https://github.com/norio-nomura) [#2038](https://github.com/realm/SwiftLint/issues/2038) -#### Bug Fixes +### Bug Fixes * Fixes an issue with the `yoda_condition` rule where the severity would always display as a warning, and the reason would display as the severity type. @@ -5241,18 +5243,18 @@ The next release will require Swift 4.0 or higher to build. ## 0.25.0: Cleaning the Lint Filter -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Adds `discouraged_optional_boolean` opt-in rule to discourage the use of optional booleans. [Ornithologist Coder](https://github.com/ornithocoder) [#2011](https://github.com/realm/SwiftLint/issues/2011) -#### Bug Fixes +### Bug Fixes * Fix some cases where `colon` rule wouldn't be autocorrected. [Manabu Nakazawa](https://github.com/mshibanami) @@ -5305,15 +5307,15 @@ The next release will require Swift 4.0 or higher to build. ## 0.24.2: Dented Tumbler -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * No longer log if the `indentation` key isn't set in the configuration file. [JP Simard](https://github.com/jpsim) @@ -5321,11 +5323,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.24.1: Dented Tumbler -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Invalidate cache when Swift patch version changes. [Norio Nomura](https://github.com/norio-nomura) @@ -5365,7 +5367,7 @@ The next release will require Swift 4.0 or higher to build. [Josep Rodriguez](https://github.com/joseprl89) [#1822](https://github.com/realm/SwiftLint/issues/1649) -#### Bug Fixes +### Bug Fixes * Fix false positives in `control_statement` rule when methods with keyword names are used. @@ -5384,7 +5386,7 @@ The next release will require Swift 4.0 or higher to build. ## 0.24.0: Timed Dry -#### Breaking +### Breaking * SwiftLint now requires Xcode 9 and Swift 3.2+ to build. [Marcelo Fabri](https://github.com/marcelofabri) @@ -5392,7 +5394,7 @@ The next release will require Swift 4.0 or higher to build. * Remove `SwiftExpressionKind.other`. [Marcelo Fabri](https://github.com/marcelofabri) -#### Enhancements +### Enhancements * Add `sorted_first_last` opt-in rule to encourage using `min()` or `max()` over `sorted().first` or `sorted().last`. @@ -5442,7 +5444,7 @@ The next release will require Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) [#1822](https://github.com/realm/SwiftLint/issues/1822) -#### Bug Fixes +### Bug Fixes * Extend `first_where` and `contains_over_first_not_nil` rules to also detect cases where calls to `filter` and `first` are parenthesized. @@ -5469,15 +5471,15 @@ The next release will require Swift 4.0 or higher to build. ## 0.23.1: Rewash: Forgotten Load Edition -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix false positive in `array_init` rule when using a `map` that doesn't take a closure. @@ -5491,11 +5493,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.23.0: Permanent Press Cycle -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Fix csv reporter to output records with new lines. [atetlaw](https://github.com/atetlaw) @@ -5545,7 +5547,7 @@ The next release will require Swift 4.0 or higher to build. [Marcelo Fabri](https://github.com/marcelofabri) [#1435](https://github.com/realm/SwiftLint/issues/1435) -#### Bug Fixes +### Bug Fixes * Improve how `opening_brace` rule reports violations locations. [Marcelo Fabri](https://github.com/marcelofabri) @@ -5586,7 +5588,7 @@ The next release will require Swift 4.0 or higher to build. ## 0.22.0: Wrinkle-free -#### Breaking +### Breaking * Nested configurations will now be merged with parent configurations rather than replace them outright. @@ -5594,7 +5596,7 @@ The next release will require Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) [#676](https://github.com/realm/SwiftLint/issues/676) -#### Enhancements +### Enhancements * Add `is_disjoint` rule to encourage using `Set.isDisjoint(with:)` over `Set.intersection(_:).isEmpty`. @@ -5663,7 +5665,7 @@ The next release will require Swift 4.0 or higher to build. [Erik Strottmann](https://github.com/erikstrottmann) [#1801](https://github.com/realm/SwiftLint/issues/1801) -#### Bug Fixes +### Bug Fixes * Fix false positive on `force_unwrapping` rule when declaring local variable with implicity unwrapped type. @@ -5693,12 +5695,12 @@ The next release will require Swift 4.0 or higher to build. ## 0.21.0: Vintage Washboard -#### Breaking +### Breaking * Xcode 8.3 or later and Swift 3.1 or later are required to build. [Norio Nomura](https://github.com/norio-nomura) -#### Enhancements +### Enhancements * Rules are now categorized as `lint`, `idiomatic`, `style`, `metrics` or `performance`. Currently this is just used for documentation purposes @@ -5805,7 +5807,7 @@ The next release will require Swift 4.0 or higher to build. [Ornithologist Coder](https://github.com/ornithocoder) [#1306](https://github.com/realm/SwiftLint/issues/1306) -#### Bug Fixes +### Bug Fixes * Fix false positive on `redundant_discardable_let` rule when using `while` statements. @@ -5841,15 +5843,15 @@ The next release will require Swift 4.0 or higher to build. ## 0.20.1: More Liquid Fabric Softener -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix typo in `FatalErrorMessageRule`. [Alexander Lash](https://github.com/abl) @@ -5876,11 +5878,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.20.0: Liquid Fabric Softener -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Detect more violations of `force_unwrapping` when using subscripts. [Otávio Lima](https://github.com/otaviolima) @@ -5929,7 +5931,7 @@ The next release will require Swift 4.0 or higher to build. [Cihat Gündüz](https://github.com/Dschee) [#1587](https://github.com/realm/SwiftLint/issues/1587) -#### Bug Fixes +### Bug Fixes * Fix false positive in `empty_enum_arguments` rule when calling methods. [Marcelo Fabri](https://github.com/marcelofabri) @@ -5967,7 +5969,7 @@ The next release will require Swift 4.0 or higher to build. ## 0.19.0: Coin-Operated Machine -#### Breaking +### Breaking * Remove support for Swift 2. [Marcelo Fabri](https://github.com/marcelofabri) @@ -5993,7 +5995,7 @@ The next release will require Swift 4.0 or higher to build. `ConditionalReturnsOnNewlineRule` to match rule naming conventions. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Cache linter results for files unmodified since the previous linter run. [Victor Pimentel](https://github.com/victorpimentel) @@ -6069,7 +6071,7 @@ The next release will require Swift 4.0 or higher to build. * Make `closure_spacing` a `CorrectableRule`. [J. Cheyo Jimenez](https://github.com/masters3d) -#### Bug Fixes +### Bug Fixes * `emoji` and `checkstyle` reporter output report sorted by file name. [norio-nomura](https://github.com/norio-nomura) @@ -6126,22 +6128,22 @@ The next release will require Swift 4.0 or higher to build. ## 0.18.1: Misaligned Drum -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Compile releases in the 'Release' configuration rather than 'Debug'. [JP Simard](https://github.com/jpsim) ## 0.18.0: Misaligned Drum -#### Breaking +### Breaking * Replace YamlSwift with Yams. SwiftLint no longer includes YamlSwift. If your project implicitly depends on YamlSwift, you need to modify it to depend on @@ -6166,7 +6168,7 @@ The next release will require Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Support compiling with Xcode 8.3 and Swift 3.1. [Keith Smiley](https://github.com/keith) @@ -6178,10 +6180,10 @@ The next release will require Swift 4.0 or higher to build. * Print YAML configuration errors in locatable format compatible with Xcode's Issue Navigator. - ![](https://cloud.githubusercontent.com/assets/33430/24688866/f18d40f4-19fd-11e7-8f17-72f1fca20406.png) + ![A list of issues and an editor open in Xcode with one line in the editor being highlighted with an error overlay.](https://cloud.githubusercontent.com/assets/33430/24688866/f18d40f4-19fd-11e7-8f17-72f1fca20406.png) [JP Simard](https://github.com/jpsim) -#### Bug Fixes +### Bug Fixes * Fix --lenient enforcement not being applied to all violations. [aaroncrespo](https://github.com/aaroncrespo) @@ -6197,7 +6199,7 @@ The next release will require Swift 4.0 or higher to build. ## 0.17.0: Extra Rinse Cycle -#### Breaking +### Breaking * `variable_name` rule (`VariableNameRule`) is now `identifier_name` (`IdentifierNameRule`) as it validates other identifiers as well. @@ -6212,7 +6214,7 @@ The next release will require Swift 4.0 or higher to build. version after important cache-related issues have been addressed. [Marcelo Fabri](https://github.com/marcelofabri) -#### Enhancements +### Enhancements * Add `implicitly_unwrapped_optional` opt-in rule that warns against using implicitly unwrapped optionals, except cases when this IUO is an IBOutlet. @@ -6291,7 +6293,7 @@ The next release will require Swift 4.0 or higher to build. [Kim de Vos](https://github.com/kimdv) [#1348](https://github.com/realm/SwiftLint/issues/1348) -#### Bug Fixes +### Bug Fixes * Fix crashes when accessing cached regular expressions when linting in parallel. @@ -6359,11 +6361,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.16.1: Commutative Fabric Sheets -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Improve `unused_optional_binding` rule on tuples check. [Rafael Machado](https://github.com/rakaramos) @@ -6381,7 +6383,7 @@ The next release will require Swift 4.0 or higher to build. [Aaron McTavish](https://github.com/aamctustwo) [#1198](https://github.com/realm/SwiftLint/issues/1198) -#### Bug Fixes +### Bug Fixes * Fix false positives on `shorthand_operator` rule. [Marcelo Fabri](https://github.com/marcelofabri) @@ -6415,13 +6417,13 @@ The next release will require Swift 4.0 or higher to build. ## 0.16.0: Maximum Energy Efficiency Setting -#### Breaking +### Breaking * Several API breaking changes were made to conform to the Swift 3 API Design Guidelines. We apologize for any inconvenience this may have caused. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Speed up linting by caching linter results across invocations. [Marcelo Fabri](https://github.com/marcelofabri) @@ -6544,7 +6546,7 @@ The next release will require Swift 4.0 or higher to build. [Rafael Machado](https://github.com/rakaramos) [#1116](https://github.com/realm/SwiftLint/issues/1116) -#### Bug Fixes +### Bug Fixes * Ignore close parentheses on `vertical_parameter_alignment` rule. [Marcelo Fabri](https://github.com/marcelofabri) @@ -6584,13 +6586,13 @@ The next release will require Swift 4.0 or higher to build. ## 0.15.0: Hand Washable Holiday Linens 🎄 -#### Breaking +### Breaking * `line_length` rule now has a default value of `120` for warnings. [Marcelo Fabri](https://github.com/marcelofabri) [#1008](https://github.com/realm/SwiftLint/issues/1008) -#### Enhancements +### Enhancements * Add `closure_end_indentation` opt-in rule that validates closure closing braces according to these rules: @@ -6623,7 +6625,7 @@ The next release will require Swift 4.0 or higher to build. [Marcelo Fabri](https://github.com/marcelofabri) [#1005](https://github.com/realm/SwiftLint/issues/1005) -#### Bug Fixes +### Bug Fixes * `FunctionParameterCountRule` also ignores generic initializers. [Mauricio Hanika](https://github.com/mAu888) @@ -6654,7 +6656,7 @@ The next release will require Swift 4.0 or higher to build. ## 0.14.0: Super Awesome Retractable Drying Rack -#### Breaking +### Breaking * SwiftLint now requires Xcode 8.x and Swift 3.x to build. APIs have not yet been adapted to conform to the Swift 3 API Design @@ -6662,7 +6664,7 @@ The next release will require Swift 4.0 or higher to build. [JP Simard](https://github.com/jpsim) [Norio Nomura](https://github.com/norio-nomura) -#### Enhancements +### Enhancements * Now builds and passes most tests on Linux using the Swift Package Manager with Swift 3. This requires `libsourcekitdInProc.so` to be built and located in @@ -6765,7 +6767,7 @@ The next release will require Swift 4.0 or higher to build. [Marcelo Fabri](https://github.com/marcelofabri) [#619](https://github.com/realm/SwiftLint/issues/619) -#### Bug Fixes +### Bug Fixes * Fix `weak_delegate` rule reporting a violation for variables containing but not ending in `delegate`. @@ -6798,11 +6800,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.13.2: Light Cycle -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * `TrailingCommaRule` now only triggers when a declaration is multi-line when using `mandatory_comma: true`. @@ -6810,7 +6812,7 @@ The next release will require Swift 4.0 or higher to build. [#910](https://github.com/realm/SwiftLint/issues/910) [#911](https://github.com/realm/SwiftLint/issues/911) -#### Bug Fixes +### Bug Fixes * Fix `MarkRule` reporting a violation for `// MARK: -`, which is valid. [JP Simard](https://github.com/jpsim) @@ -6818,11 +6820,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.13.1: Heavy Cycle -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Add `ImplicitGetterRule` to warn against using `get` on computed read-only properties. @@ -6870,7 +6872,7 @@ The next release will require Swift 4.0 or higher to build. * Make `MarkRule` correctable. [kohtenko](https://github.com/kohtenko) -#### Bug Fixes +### Bug Fixes * Rule out a few invalid `@IBInspectable` cases in `valid_ibinspectable`. [Daniel Duan](https://github.com/dduan) @@ -6903,11 +6905,11 @@ The next release will require Swift 4.0 or higher to build. ## 0.13.0: MakeYourClothesCleanAgain -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Add `ignores_comment` configuration for `trailing_whitespace` rule. [Javier Hernández](https://github.com/jaherhi) @@ -6959,7 +6961,7 @@ The next release will require Swift 4.0 or higher to build. [Matt Taube](https://github.com/mtaube) [#715](https://github.com/realm/SwiftLint/pull/715) -#### Bug Fixes +### Bug Fixes * Fixed whitespace being added to TODO messages. [W. Bagdon](https://github.com/wbagdon) @@ -7000,20 +7002,20 @@ The next release will require Swift 4.0 or higher to build. ## 0.12.0: Vertical Laundry -#### Breaking +### Breaking * Fixed: SwiftLint assumes paths in the YAML config file are relative to the current directory even when `--path` is passed as an argument. [Cristian Filipov](https://github.com/cfilipov) -#### Enhancements +### Enhancements * Add `--enable-all-rules` CLI option to `lint` command to facilitate running all rules, even opt-in and disabled ones, ignoring `whitelist_rules`. [JP Simard](https://github.com/jpsim) [#1170](https://github.com/realm/SwiftLint/issues/1170) -#### Bug Fixes +### Bug Fixes * Made Vertical Whitespace Rule added in 0.11.2 opt-in due to performance issues. @@ -7024,11 +7026,11 @@ The next release will require Swift 4.0 or higher to build. This release has seen a phenomenal uptake in community contributions! -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Add `MarkRule` rule to enforce `// MARK` syntax. [Krzysztof Rodak](https://github.com/krodak) @@ -7083,7 +7085,7 @@ This release has seen a phenomenal uptake in community contributions! [Mohpor](https://github.com/mohpor) [#557](https://github.com/realm/SwiftLint/issues/557) -#### Bug Fixes +### Bug Fixes * Fixed CustomRule Regex. [J. Cheyo Jimenez](https://github.com/masters3d) @@ -7102,13 +7104,13 @@ This release has seen a phenomenal uptake in community contributions! [Daniel Beard](https://github.com/daniel-beard) [#721](https://github.com/realm/SwiftLint/issues/721) -## 0.11.1: Cuddles... Or Else! +## 0.11.1: Cuddles... Or Else! -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Added `statement_mode` configuration to the `statement_position` rule. The `default` mode keeps the current SwiftLint behavior of keeping `else` and @@ -7118,7 +7120,7 @@ This release has seen a phenomenal uptake in community contributions! [Mike Skiba](https://github.com/ateliercw) [#651](https://github.com/realm/SwiftLint/issues/651) -#### Bug Fixes +### Bug Fixes * Remove extraneous argument label added in LegacyCGGeometryFunctionsRule autocorrect. @@ -7127,7 +7129,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.11.0: Laundromat Format -#### Breaking +### Breaking * Now `type_name` allows lowercase enum values to match the Swift API Design Guidelines. @@ -7141,7 +7143,7 @@ This release has seen a phenomenal uptake in community contributions! multiple copies of the Swift libraries. [Norio Nomura](https://github.com/norio-nomura) -#### Enhancements +### Enhancements * Add `--format` option to `autocorrect` command which re-indents Swift files much like pasting into Xcode would. This option isn't currently configurable, @@ -7157,7 +7159,7 @@ This release has seen a phenomenal uptake in community contributions! lines. Defaults to `false`. Added unit tests. [Reimar Twelker](https://github.com/raginmari) -#### Bug Fixes +### Bug Fixes * Fix false positive in conditional binding cascade violation. [Norio Nomura](https://github.com/norio-nomura) @@ -7175,11 +7177,11 @@ This release has seen a phenomenal uptake in community contributions! ## 0.10.0: `laundry-select` edition -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Now `libclang.dylib` and `sourcekitd.framework` are dynamically loaded at runtime by SourceKittenFramework to use the versions included in the Xcode @@ -7201,7 +7203,7 @@ This release has seen a phenomenal uptake in community contributions! * Add autocorrect for `ReturnArrowWhitespaceRule`. [Craig Siemens](https://github.com/CraigSiemens) -#### Bug Fixes +### Bug Fixes * Failed to launch swiftlint when Xcode.app was placed at non standard path. [Norio Nomura](https://github.com/norio-nomura) @@ -7217,11 +7219,11 @@ This release has seen a phenomenal uptake in community contributions! ## 0.9.2: Multiple Exhaust Codes -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Return different exit codes to distinguish between types of errors: * 0: No errors, maybe warnings in non-strict mode @@ -7238,7 +7240,7 @@ This release has seen a phenomenal uptake in community contributions! [Erik Aigner](https://github.com/eaigner) [#566](https://github.com/realm/SwiftLint/issues/566) -#### Bug Fixes +### Bug Fixes * Avoid overwriting files whose contents have not changed. [Neil Gall](https://github.com/neilgall) @@ -7255,15 +7257,15 @@ This release has seen a phenomenal uptake in community contributions! ## 0.9.1: Air Duct Cleaning -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix force unwrap rule missed cases with quotes. [Norio Nomura](https://github.com/norio-nomura) @@ -7275,7 +7277,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.9.0: Appliance Maintenance -#### Breaking +### Breaking * `Linter.reporter` has been removed and `Configuration.reporterFromString(_:)` has been renamed to a free function: `reporterFromString(_:)`. @@ -7313,7 +7315,7 @@ This release has seen a phenomenal uptake in community contributions! is now ignored. Nested configuration files are now always considered. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * `swiftlint lint` now accepts an optional `--reporter` parameter which overrides existing `reporter` values in the configuration file. Choose between @@ -7325,7 +7327,7 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) * `lint` and `autocorrect` commands now accept a `--quiet` flag that prevents - status messages like 'Linting ' & 'Done linting' from being logged. + status messages like `Linting ` & `Done linting` from being logged. [JP Simard](https://github.com/jpsim) [#386](https://github.com/realm/SwiftLint/issues/386) @@ -7341,7 +7343,7 @@ This release has seen a phenomenal uptake in community contributions! * Improve performance of `ColonRule`. [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * Fix case sensitivity of keywords for `valid_docs`. [Ankit Aggarwal](https://github.com/aciidb0mb3r) @@ -7370,7 +7372,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.8.0: High Heat -#### Breaking +### Breaking * Setting only warning on `SeverityLevelsConfig` rules now disables the error value. @@ -7380,7 +7382,7 @@ This release has seen a phenomenal uptake in community contributions! * `enabled_rules` has been renamed to `opt_in_rules`. [Daniel Beard](https://github.com/daniel-beard) -#### Enhancements +### Enhancements * Add `whitelist_rules` rule whitelists in config files. [Daniel Beard](https://github.com/daniel-beard) @@ -7416,7 +7418,7 @@ This release has seen a phenomenal uptake in community contributions! * Reduce maximum memory usage. [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * Fix more false positives in `ValidDocsRule`. [diogoguimaraes](https://github.com/diogoguimaraes) @@ -7433,15 +7435,15 @@ This release has seen a phenomenal uptake in community contributions! ## 0.7.2: Appliance Manual -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Fix several false positives in `ValidDocsRule`. [diogoguimaraes](https://github.com/diogoguimaraes) @@ -7449,11 +7451,11 @@ This release has seen a phenomenal uptake in community contributions! ## 0.7.1: Delicate Cycle -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Improve performance of `MissingDocsRule`. [Norio Nomura](https://github.com/norio-nomura) @@ -7467,7 +7469,7 @@ This release has seen a phenomenal uptake in community contributions! [Benjamin Otto](https://github.com/Argent) [#55](https://github.com/realm/SwiftLint/issues/55) -#### Bug Fixes +### Bug Fixes * Fix several false positives in `ValidDocsRule`. [diogoguimaraes](https://github.com/diogoguimaraes) @@ -7475,7 +7477,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.7.0: Automatic Permanent Press -#### Breaking +### Breaking * Replaced all uses of `XPCDictionary` with `[String: SourceKitRepresentable]`. @@ -7489,7 +7491,7 @@ This release has seen a phenomenal uptake in community contributions! by `ConfigProviderRule` and `SeverityLevelsConfig`. [Scott Hoyt](https://github.com/scottrhoyt) -#### Enhancements +### Enhancements * `TypeBodyLengthRule` now does not count comment or whitespace lines. [Marcelo Fabri](https://github.com/marcelofabri) @@ -7515,7 +7517,7 @@ This release has seen a phenomenal uptake in community contributions! * Add `CyclomaticComplexityRule`. [Denis Lebedev](https://github.com/garnett) -#### Bug Fixes +### Bug Fixes * Fix crash caused by infinite recursion when using nested config files. [JP Simard](https://github.com/jpsim) @@ -7527,7 +7529,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.6.0: Steam Cycle -#### Breaking +### Breaking * `ParameterizedRule` is removed. Use `ConfigurableRule` instead. [Scott Hoyt](https://github.com/scottrhoyt) @@ -7536,7 +7538,7 @@ This release has seen a phenomenal uptake in community contributions! * To activate a `Rule`, it must be added to the global `masterRuleList`. [Scott Hoyt](https://github.com/scottrhoyt) -#### Enhancements +### Enhancements * `ConfigurableRule` protocol allows for improved rule configuration. See `CONTRIBUTING` for more details. @@ -7584,22 +7586,22 @@ This release has seen a phenomenal uptake in community contributions! * Add opt-in "Missing Docs" rule to detect undocumented public declarations. [JP Simard](https://github.com/jpsim) -#### Bug Fixes +### Bug Fixes * None. ## 0.5.6: Bug FixLint -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Improve performance by reducing calls to SourceKit. [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * Fix homebrew deployment issues. [Norio Nomura](https://github.com/norio-nomura) @@ -7618,15 +7620,15 @@ This release has seen a phenomenal uptake in community contributions! -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Always fail if a YAML configuration file was found but could not be parsed. [JP Simard](https://github.com/jpsim) @@ -7638,13 +7640,13 @@ This release has seen a phenomenal uptake in community contributions! ## 0.5.4: Bounce™ -#### Breaking +### Breaking * Remove `Location.init(file:offset:)` in favor of the more explicit `Location.init(file:byteOffset:)` & `Location.init(file:characterOffset:)`. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Add `checkstyle` reporter to generate XML reports in the Checkstyle 4.3 format. @@ -7660,7 +7662,7 @@ This release has seen a phenomenal uptake in community contributions! [Scott Hoyt](https://github.com/scottrhoyt) [#299](https://github.com/realm/SwiftLint/issues/299) -#### Bug Fixes +### Bug Fixes * Fix multibyte handling in many rules. [JP Simard](https://github.com/jpsim) @@ -7676,11 +7678,11 @@ This release has seen a phenomenal uptake in community contributions! ## 0.5.3: Mountain Scent -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Improve autocorrect for OpeningBraceRule. [Yasuhiro Inami](https://github.com/inamiy) @@ -7691,7 +7693,7 @@ This release has seen a phenomenal uptake in community contributions! * Add ClosingBraceRule. [Yasuhiro Inami](https://github.com/inamiy) -#### Bug Fixes +### Bug Fixes * Fix false positives in ValidDocsRule. [JP Simard](https://github.com/jpsim) @@ -7699,16 +7701,16 @@ This release has seen a phenomenal uptake in community contributions! ## 0.5.2: Snuggle™ -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Performance improvements & unicode fixes (via SourceKitten). [Norio Nomura](https://github.com/norio-nomura) -#### Bug Fixes +### Bug Fixes * Fix `ValidDocsRule` false positive when documenting functions with closure parameters. @@ -7717,15 +7719,15 @@ This release has seen a phenomenal uptake in community contributions! ## 0.5.1: Lint Tray Malfunction -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * None. -#### Bug Fixes +### Bug Fixes * Make linting faster than 0.5.0, but slower than 0.4.0. [Norio Nomura](https://github.com/norio-nomura) @@ -7739,12 +7741,12 @@ This release has seen a phenomenal uptake in community contributions! ## 0.5.0: Downy™ -#### Breaking +### Breaking * `init()` is no longer a member of the `Rule` protocol. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Add legacy constructor rule. [Marcelo Fabri](https://github.com/marcelofabri) @@ -7765,7 +7767,7 @@ This release has seen a phenomenal uptake in community contributions! * Allow to exclude files from `included` directory with `excluded`. [Michal Laskowski](https://github.com/michallaskowski) -#### Bug Fixes +### Bug Fixes * Statement position rule no longer triggers for non-keyword uses of `catch` and `else`. @@ -7778,14 +7780,14 @@ This release has seen a phenomenal uptake in community contributions! ## 0.4.0: Wrinkle Release -#### Breaking +### Breaking * API: Rename RuleExample to RuleDescription, remove StyleViolationType and combine Rule().identifier and Rule().example into Rule.description. [JP Simard](https://github.com/jpsim) [#183](https://github.com/realm/SwiftLint/issues/183) -#### Enhancements +### Enhancements * The `VariableNameRule` now allows capitalized variable names when they are declared static. This allows stylistic usage common in cases like @@ -7807,7 +7809,7 @@ This release has seen a phenomenal uptake in community contributions! [Norio Nomura](https://github.com/norio-nomura) [#193](https://github.com/realm/SwiftLint/pull/193) -#### Bug Fixes +### Bug Fixes * All rules now print their identifiers in reports. [JP Simard](https://github.com/jpsim) @@ -7837,13 +7839,13 @@ This release has seen a phenomenal uptake in community contributions! ## 0.3.0: Wrinkly Rules -#### Breaking +### Breaking * `swiftlint rules` now just prints a list of all available rules and their identifiers. [JP Simard](https://github.com/jpsim) -#### Enhancements +### Enhancements * Support for Swift 2.1. [JP Simard](https://github.com/jpsim) @@ -7877,7 +7879,7 @@ This release has seen a phenomenal uptake in community contributions! * Lint parameter variables. [JP Simard](https://github.com/jpsim) -#### Bug Fixes +### Bug Fixes * Custom reporters are now supported even when not running with `--use-stdin`. [JP Simard](https://github.com/jpsim) @@ -7889,7 +7891,7 @@ This release has seen a phenomenal uptake in community contributions! ## 0.2.0: Tumble Dry -#### Breaking +### Breaking * SwiftLint now exclusively supports Swift 2.0. [JP Simard](https://github.com/jpsim) @@ -7900,7 +7902,7 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#113](https://github.com/realm/SwiftLint/issues/113) -#### Enhancements +### Enhancements * Configure SwiftLint via a YAML file: Supports `disabled_rules`, `included`, `excluded` and passing parameters to @@ -7934,7 +7936,7 @@ This release has seen a phenomenal uptake in community contributions! [JP Simard](https://github.com/jpsim) [#42](https://github.com/realm/SwiftLint/issues/42) -#### Bug Fixes +### Bug Fixes * Improve performance of `TrailingWhitespaceRule`. [Keith Smiley](https://github.com/keith) @@ -7944,11 +7946,11 @@ This release has seen a phenomenal uptake in community contributions! ## 0.1.2: FabricSoftenerRule -#### Breaking +### Breaking * None. -#### Enhancements +### Enhancements * Added `OperatorFunctionWhitespaceRule` to make sure that you use whitespace around operators when defining them. @@ -7969,13 +7971,13 @@ This release has seen a phenomenal uptake in community contributions! * Lint parentheses around switch statements. [Keith Smiley](https://github.com/keith) -#### Bug Fixes +### Bug Fixes * None. ## 0.1.1: Top Loading -#### Breaking +### Breaking * The `Rule` and `ASTRule` protocol members are now non-static. [aarondaub](https://github.com/aarondaub) @@ -7984,7 +7986,7 @@ This release has seen a phenomenal uptake in community contributions! [aarondaub](https://github.com/aarondaub) [#21](https://github.com/realm/SwiftLint/issues/21) -#### Enhancements +### Enhancements * Added a command line option `--path` to specify a path to lint. [Lars Lockefeer](https://github.com/larslockefeer) @@ -8014,7 +8016,7 @@ This release has seen a phenomenal uptake in community contributions! FunctionBodyLength, Nesting, TypeBodyLength, TypeName, VariableName. [JP Simard](https://github.com/jpsim) -#### Bug Fixes +### Bug Fixes * Trailing newline and file length violations are now displayed in Xcode. [JP Simard](https://github.com/jpsim) @@ -8023,3 +8025,5 @@ This release has seen a phenomenal uptake in community contributions! ## 0.1.0: Fresh Out Of The Dryer First Version! + + diff --git a/tools/add-new-changelog-section.sh b/tools/add-new-changelog-section.sh index fae8392ac9..62963c41ea 100755 --- a/tools/add-new-changelog-section.sh +++ b/tools/add-new-changelog-section.sh @@ -2,33 +2,32 @@ set -euo pipefail -# Text to prepend +# Header with new section new_section=$(cat < "$temp_file" +# Read changelog skipping the first line +changelog=$(tail -n +2 CHANGELOG.md) -# Replace the changelog file with this new file -mv "$temp_file" CHANGELOG.md +# Prepend the new section and a newline to the existing changelog +{ echo -e "$new_section"; echo "$changelog"; } > CHANGELOG.md diff --git a/tools/create-github-release.sh b/tools/create-github-release.sh index 99ddeb9269..fb81bf0802 100755 --- a/tools/create-github-release.sh +++ b/tools/create-github-release.sh @@ -11,7 +11,7 @@ release_notes=$(mktemp) # Create GitHub Release -release_title="$(sed -n '1s/^## //p' CHANGELOG.md)" +release_title="$(sed -n '3s/^## //p' CHANGELOG.md)" gh release create "$version" --title "$release_title" -F "$release_notes" --draft --verify-tag \ "bazel.tar.gz" \ "bazel.tar.gz.sha256" \ From 995f8e17b1fc98fbaa1c1311ce3d822720a8525e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 22:33:27 +0100 Subject: [PATCH 221/260] Fetch before checkout --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf76bd7c6c..697a2b2a86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create release branch - run: git checkout ${{ env.RELEASE_BRANCH }} || git checkout -b ${{ env.RELEASE_BRANCH }} + run: >- + git fetch origin ${{ env.RELEASE_BRANCH }} + && git checkout ${{ env.RELEASE_BRANCH }} + || git checkout -b ${{ env.RELEASE_BRANCH }} - name: Update changelog run: "sed -i 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions From 9a7e645a02f3c91dadbc4317c4374e5b8c87ef84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 23:00:19 +0100 Subject: [PATCH 222/260] Build Docker image from local context --- .github/workflows/docker.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 930491f762..cf320f6424 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -33,9 +33,6 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ env.CHECKOUT_REF }} - - name: Update branch - if: github.event_name != 'push' - run: git pull - name: Set lowercase repository name run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> $GITHUB_ENV env: @@ -50,6 +47,7 @@ jobs: registry: ghcr.io - uses: docker/build-push-action@v6 with: + context: . tags: ghcr.io/${{ env.REPOSITORY_LC }}:${{ env.DOCKER_TAG }} platforms: linux/amd64 outputs: | From a08c92c870d14b3ad1e212eec6f4c7ea5cae6855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 23:04:57 +0100 Subject: [PATCH 223/260] Separate Docker build into artifact and release version This builds the Docker image twice per release - first to extract the Linux binary from it as a dedicated release artifact, second as the official Docker image associated with the later published release. This avoids that a Docker image for a version exist that is not otherwise available as an official release on GitHub. --- .github/workflows/docker.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index cf320f6424..d529504cdf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,6 +4,8 @@ on: push: branches: - main + release: + types: published workflow_call: inputs: ref: @@ -25,11 +27,19 @@ jobs: run: | echo "CHECKOUT_REF=main" >> $GITHUB_ENV echo "DOCKER_TAG=latest" >> $GITHUB_ENV + echo "OUTPUT_TYPE='type=registry'" >> $GITHUB_ENV + - name: Define variable on release event + if: github.event_name == 'release' + run: | + echo "CHECKOUT_REF=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + echo "DOCKER_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + echo "OUTPUT_TYPE='type=registry'" >> $GITHUB_ENV - name: Define variables on workflow call - if: github.event_name != 'push' + if: github.event_name != 'push' && github.event_name != 'release' run: | echo "CHECKOUT_REF=${{ inputs.ref }}" >> $GITHUB_ENV echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV + echo "OUTPUT_TYPE='type=local,dest=artifacts'" >> $GITHUB_ENV - uses: actions/checkout@v4 with: ref: ${{ env.CHECKOUT_REF }} @@ -50,13 +60,12 @@ jobs: context: . tags: ghcr.io/${{ env.REPOSITORY_LC }}:${{ env.DOCKER_TAG }} platforms: linux/amd64 - outputs: | - type=registry - type=local,dest=artifacts + outputs: ${{ env.OUTPUT_TYPE }} - name: Rename binary artifact + if: contains(env.OUTPUT_TYPE, 'local') run: mv artifacts/usr/bin/swiftlint artifacts/usr/bin/swiftlint_linux_amd64 - name: Upload binary artifact - if: github.event_name != 'push' + if: contains(env.OUTPUT_TYPE, 'local') uses: actions/upload-artifact@v4 with: name: swiftlint_linux_amd64 From d8b72158445b6649bad0ca05fb49757ea94330e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 23:08:12 +0100 Subject: [PATCH 224/260] Remove quotes --- .github/workflows/docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d529504cdf..9dfa9d750a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -27,19 +27,19 @@ jobs: run: | echo "CHECKOUT_REF=main" >> $GITHUB_ENV echo "DOCKER_TAG=latest" >> $GITHUB_ENV - echo "OUTPUT_TYPE='type=registry'" >> $GITHUB_ENV + echo "OUTPUT_TYPE=type=registry" >> $GITHUB_ENV - name: Define variable on release event if: github.event_name == 'release' run: | echo "CHECKOUT_REF=${{ github.event.release.tag_name }}" >> $GITHUB_ENV echo "DOCKER_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - echo "OUTPUT_TYPE='type=registry'" >> $GITHUB_ENV + echo "OUTPUT_TYPE=type=registry" >> $GITHUB_ENV - name: Define variables on workflow call if: github.event_name != 'push' && github.event_name != 'release' run: | echo "CHECKOUT_REF=${{ inputs.ref }}" >> $GITHUB_ENV echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - echo "OUTPUT_TYPE='type=local,dest=artifacts'" >> $GITHUB_ENV + echo "OUTPUT_TYPE=type=local,dest=artifacts" >> $GITHUB_ENV - uses: actions/checkout@v4 with: ref: ${{ env.CHECKOUT_REF }} From 4906fa58b300c75685a66873ffa85ab0b924d6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 16 Jan 2025 22:18:23 +0100 Subject: [PATCH 225/260] Add changelog entry --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 404171fc93..9d25d41d94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,9 @@ ### Bug Fixes -* None. +* Fix version being reported by the binary consumed from the Docker image. + [SimplyDanny](https://github.com/SimplyDanny) + [#5966](https://github.com/realm/SwiftLint/issues/5966) ## 0.58.1: New Year’s Fresh Fold From 81229e555451591d1e8cfaa0a3fe01cacd5b9c1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:10:28 +0000 Subject: [PATCH 226/260] Prepare 0.58.2 release --- CHANGELOG.md | 2 +- MODULE.bazel | 2 +- Source/SwiftLintFramework/Models/Version.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d25d41d94..35e8c02523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Main +## 0.58.2: New Year’s Fresh Fold ### Breaking diff --git a/MODULE.bazel b/MODULE.bazel index 2c6ab25ad8..5ec8e0f4ea 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "swiftlint", - version = "0.58.1", + version = "0.58.2", compatibility_level = 1, repo_name = "SwiftLint", ) diff --git a/Source/SwiftLintFramework/Models/Version.swift b/Source/SwiftLintFramework/Models/Version.swift index adcecda47e..9ff5385c3c 100644 --- a/Source/SwiftLintFramework/Models/Version.swift +++ b/Source/SwiftLintFramework/Models/Version.swift @@ -9,7 +9,7 @@ public struct Version: VersionComparable, Sendable { } /// The current SwiftLint version. - public static let current = Self(value: "0.58.1") + public static let current = Self(value: "0.58.2") /// Public initializer. /// From eba420f77846e93beb98d516b225abeb2fef4ca2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:25:20 +0000 Subject: [PATCH 227/260] Release 0.58.2 --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 148dde7024..e0447c706b 100644 --- a/Package.swift +++ b/Package.swift @@ -195,8 +195,8 @@ let package = Package( package.targets.append( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.58.1/SwiftLintBinary.artifactbundle.zip", - checksum: "ce4c20a4107cb13ebdc25bad91307c78451df4df957a0727c249122587984ff5" + url: "https://github.com/realm/SwiftLint/releases/download/0.58.2/SwiftLintBinary.artifactbundle.zip", + checksum: "f2de7c148dba39bf0ad55ada8f60b15dde383c643c69f7eb2448bd2ed532f659" ) ) #endif From bdcff41f0bd473ee9b9b9865c6821fb0df571692 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:25:26 +0000 Subject: [PATCH 228/260] Add new changelog section --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e8c02523..9d976780c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## Main + +### Breaking + +* None. + +### Experimental + +* None. + +### Enhancements + +* None. + +### Bug Fixes + +* None. + ## 0.58.2: New Year’s Fresh Fold ### Breaking From 18d658bbf3ba378ba107d1d2cee6980eb9246326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 17 Jan 2025 00:02:16 +0100 Subject: [PATCH 229/260] Specify explicit OS version --- .github/workflows/docker.yml | 2 +- .github/workflows/plugins-sync.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9dfa9d750a..01a5096ad1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -28,7 +28,7 @@ jobs: echo "CHECKOUT_REF=main" >> $GITHUB_ENV echo "DOCKER_TAG=latest" >> $GITHUB_ENV echo "OUTPUT_TYPE=type=registry" >> $GITHUB_ENV - - name: Define variable on release event + - name: Define variables on release event if: github.event_name == 'release' run: | echo "CHECKOUT_REF=${{ github.event.release.tag_name }}" >> $GITHUB_ENV diff --git a/.github/workflows/plugins-sync.yml b/.github/workflows/plugins-sync.yml index 0fb2a50ef8..bdd3d8fc5e 100644 --- a/.github/workflows/plugins-sync.yml +++ b/.github/workflows/plugins-sync.yml @@ -11,7 +11,7 @@ on: jobs: sync: name: Sync Plugins Folder - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v4 From a1bff9beea1a115d4823c9e0914f167875042c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 17 Jan 2025 22:30:03 +0100 Subject: [PATCH 230/260] Set up Homebrew before version bump --- .github/workflows/post-release.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index af33532f81..314c7b7990 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -48,14 +48,23 @@ jobs: bump-homebrew: name: Bump Homebrew Formula runs-on: ubuntu-24.04 + container: + image: ghcr.io/homebrew/ubuntu24.04:latest steps: - name: Retrieve author in uppercase id: retrieve_author run: | AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - - name: Prepare Homebrew - run: eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + - name: Set up Homebrew + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master + with: + test-bot: false + - name: Configure Git author + uses: Homebrew/actions/git-user-config@master + with: + token: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} - name: Update Homebrew formula uses: Homebrew/actions/bump-packages@master with: From a1c27db5b4242ecdb91c923a8d5c5962358607d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 17 Jan 2025 22:32:06 +0100 Subject: [PATCH 231/260] Retrieve author information with Homebrew action --- .github/workflows/release.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 697a2b2a86..a869c93968 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,8 @@ jobs: prepare-release: name: Prepare Release runs-on: ubuntu-24.04 + outputs: + author_uppercase: ${{ steps.retrieve_author.outputs.name }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -36,10 +38,20 @@ jobs: run: | sed 's/__VERSION__/${{ inputs.version }}/g' tools/Version.swift.template > Source/SwiftLintFramework/Models/Version.swift sed -i -e '3s/.*/ version = "${{ inputs.version }}",/' MODULE.bazel + - name: Retrieve author in uppercase + id: retrieve_author + run: | + AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') + echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + - name: Configure Git author + id: configure_git_author + uses: Homebrew/actions/git-user-config@master + with: + token: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} - name: Configure author run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "${{ steps.configure_git_author.outputs.name }}" + git config --local user.email "${{ steps.configure_git_author.outputs.email }}" - name: Commit changes id: pre_release run: | @@ -76,6 +88,7 @@ jobs: create-release: name: Create Release needs: + - prepare-release - build-docker - build-macos runs-on: macOS-14 @@ -117,15 +130,10 @@ jobs: git tag -a "${{ inputs.version }}" -m "${{ inputs.title }}" git push origin HEAD git push origin "${{ inputs.version }}" - - name: Retrieve author in uppercase - id: retrieve_author - run: | - AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') - echo "name=${AUTHOR}" >> $GITHUB_OUTPUT - name: Create release run: ./tools/create-github-release.sh "${{ inputs.version }}" env: - GITHUB_TOKEN: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} + GITHUB_TOKEN: ${{ secrets[format('PERSONAL_GITHUB_TOKEN_{0}', needs.prepare-release.outputs.author_uppercase)] }} - name: Add new changelog section run: | ./tools/add-new-changelog-section.sh From 77a76a2f32c836f32150e4c5cf403e4e05458166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 17 Jan 2025 22:55:30 +0100 Subject: [PATCH 232/260] Make use of author information in later release steps --- .github/workflows/release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a869c93968..4e7268cf12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,9 @@ jobs: name: Prepare Release runs-on: ubuntu-24.04 outputs: + author_name: ${{ steps.configure_git_author.outputs.name }} author_uppercase: ${{ steps.retrieve_author.outputs.name }} + author_email: ${{ steps.configure_git_author.outputs.email }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -98,8 +100,8 @@ jobs: ref: ${{ env.RELEASE_BRANCH }} - name: Configure author run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "${{ needs.prepare-release.outputs.author_name }}" + git config --local user.email "${{ needs.prepare-release.outputs.author_email }}" - name: Create build folders run: mkdir -p ${{ env.MACOS_BUILD_DIR }} ${{ env.LINUX_BUILD_DIR }} - name: Download binary artifact for macOS From f6e4adc065e690f8b898addb3531213a00fc155a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 18 Jan 2025 14:00:27 +0100 Subject: [PATCH 233/260] Run Pod publishing on Xcode 15.0.1 CocoaPods selects the oldest SDK/simulator available on a machine. On Github Action runners, this would be iOS 17, which only works with Xcode 15.0.1. --- .github/workflows/post-release.yml | 2 +- .github/workflows/release.yml | 6 ++++++ Makefile | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 314c7b7990..d3a8178df2 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -20,7 +20,7 @@ jobs: - name: Deploy to CocoaPods run: make pod_publish env: - DEVELOPER_DIR: /Applications/Xcode_16.2.app + DEVELOPER_DIR: /Applications/Xcode_15.0.1.app COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} dispatch-plugins: name: Dispatch Plugins Repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e7268cf12..703a8d5c1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,12 @@ jobs: git fetch origin ${{ env.RELEASE_BRANCH }} && git checkout ${{ env.RELEASE_BRANCH }} || git checkout -b ${{ env.RELEASE_BRANCH }} + - name: Lint Podspec # Make sure Podspec still builds okay on CI with old release. + run: | + make pod_lint + grep -q 'DEVELOPER_DIR: ${{ env.DEVELOPER_DIR }}' .github/workflows/post-release.yml || echo "Xcode version not in sync with post-release workflow" + env: + DEVELOPER_DIR: /Applications/Xcode_15.0.1.app # Supports iOS 17.0 simulator selected by CocoaPods. - name: Update changelog run: "sed -i 's/## Main/## ${{ inputs.version }}: ${{ inputs.title }}/g' CHANGELOG.md" - name: Update built-in versions diff --git a/Makefile b/Makefile index 9fa3fbbaf2..319f0e58e7 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,10 @@ pod_publish: bundle install bundle exec pod trunk push SwiftLint.podspec +pod_lint: + bundle install + bundle exec pod lib lint SwiftLint.podspec + docs: swift run swiftlint generate-docs bundle install From 22ccb80444679484dce89f88052e77dd2b77e5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 18 Jan 2025 16:41:39 +0100 Subject: [PATCH 234/260] Run more Linux checks on CI (#5963) * SPM version tests * Plugin build checks * Bazel build and test --- azure-pipelines.yml | 65 ++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 323dff9baa..f4d9169935 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,33 +5,41 @@ variables: CI: 'true' jobs: -- job: Ubuntu +- job: spm_linux + displayName: 'SPM, Linux : Swift 6' pool: vmImage: 'ubuntu-24.04' # "Noble Numbat" - strategy: - maxParallel: 10 - matrix: - 'Swift 6': - image: swift:6.0-noble - container: $[ variables['image'] ] + container: swift:6.0-noble steps: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test -- job: macOS +- job: bazel_linux + displayName: 'Bazel, Linux : Swift 6' + pool: + vmImage: 'ubuntu-24.04' + steps: + - script: | + export PATH="/usr/share/swift/usr/bin:$PATH" + git apply --ignore-whitespace .bcr/patches/no-warnings-as-errors.patch + bazel build :swiftlint + displayName: bazel test + env: + CC: "clang" + +- job: tests_macos + displayName: 'Tests, macOS' strategy: - maxParallel: 10 + maxParallel: '10' matrix: - '14, Xcode 15.4': + '14 : Xcode 15.4': image: 'macOS-14' xcode: '15.4' - '14, Xcode 16.1': - image: 'macOS-14' - xcode: '16.1' - '15, Xcode 15.4': + # '14 : Xcode 16.2': Runs on Buildkite. + '15 : Xcode 15.4': image: 'macOS-15' xcode: '15.4' - '15, Xcode 16.2': + '15 : Xcode 16.2': image: 'macOS-15' xcode: '16.2' pool: @@ -42,23 +50,20 @@ jobs: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES displayName: swift test -- job: Plugins # Plugins shall be able to run on older Swift versions. +- job: plugins_linux # Plugins shall be able to run on older Swift versions. + displayName: 'Plugins, Linux' + pool: + vmImage: 'ubuntu-24.04' # "Noble Numbat" strategy: - maxParallel: 10 + maxParallel: '10' matrix: - ': macOS 13, Swift 5.9': - image: 'macOS-13' - xcode: '15.2' - ': macOS 14, Swift 5.10': - image: 'macOS-14' - xcode: '15.4' - ': macOS 14, Swift 6': - image: 'macOS-14' - xcode: '16.1' - pool: - vmImage: $(image) - variables: - DEVELOPER_DIR: /Applications/Xcode_$(xcode).app + ': Swift 5.9': + image: swift:5.9-focal + ': Swift 5.10': + image: swift:5.10-noble + ': Swift 6': + image: swift:6.0-noble + container: $[ variables['image'] ] steps: - script: swift build -c release --product SwiftLintCommandPlugin displayName: Command Plugin From dd48200b48d710a1d571e475401e72d6eb3b35b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 18 Jan 2025 17:13:48 +0100 Subject: [PATCH 235/260] Polish display names of pipeline steps (#5971) --- azure-pipelines.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f4d9169935..2bb78a9448 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,7 +12,7 @@ jobs: container: swift:6.0-noble steps: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES - displayName: swift test + displayName: Run tests - job: bazel_linux displayName: 'Bazel, Linux : Swift 6' @@ -23,7 +23,7 @@ jobs: export PATH="/usr/share/swift/usr/bin:$PATH" git apply --ignore-whitespace .bcr/patches/no-warnings-as-errors.patch bazel build :swiftlint - displayName: bazel test + displayName: Build SwiftLint with Bazel env: CC: "clang" @@ -48,7 +48,7 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_$(xcode).app steps: - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES - displayName: swift test + displayName: Run tests - job: plugins_linux # Plugins shall be able to run on older Swift versions. displayName: 'Plugins, Linux' @@ -66,9 +66,9 @@ jobs: container: $[ variables['image'] ] steps: - script: swift build -c release --product SwiftLintCommandPlugin - displayName: Command Plugin + displayName: Build command plugin - script: swift build -c release --product SwiftLintBuildToolPlugin - displayName: Build Tool Plugin + displayName: Build build tool plugin - job: CocoaPods pool: @@ -77,11 +77,11 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_16.1.app steps: - script: bundle install --path vendor/bundle - displayName: bundle install + displayName: Install dependencies - script: bundle exec pod repo update - displayName: pod repo update + displayName: Update CocoaPods repo - script: bundle exec pod lib lint --platforms=macos --verbose - displayName: pod lib lint + displayName: Lint Podspec for macOS - job: Jazzy pool: @@ -90,11 +90,11 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_15.4.app steps: - script: swift run swiftlint generate-docs - displayName: Run swiftlint generate-docs + displayName: Generate documentation - script: bundle install --path vendor/bundle - displayName: bundle install + displayName: Install dependencies - script: bundle exec jazzy - displayName: Run jazzy + displayName: Run Jazzy - script: > if ruby -rjson -e "j = JSON.parse(File.read('docs/undocumented.json')); exit j['warnings'].length != 0"; then echo "Undocumented declarations:" @@ -106,10 +106,12 @@ jobs: inputs: artifactName: 'API Docs' targetPath: 'docs' + displayName: Publish API docs - task: DownloadSecureFile@1 condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') inputs: secureFile: doc_deploy_key + displayName: Download deploy key - script: ./tools/push-docs - displayName: Publish + displayName: Push documentation to GitHub Pages condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') From b52aea7752767ebd2d3934003912acb4a1d5425b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 19 Jan 2025 13:20:39 +0100 Subject: [PATCH 236/260] Remove CocoaPods lint job from PR builds in favor of a single pre-release check --- Makefile | 2 +- azure-pipelines.yml | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 319f0e58e7..f0b09ff520 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,7 @@ pod_publish: pod_lint: bundle install - bundle exec pod lib lint SwiftLint.podspec + bundle exec pod lib lint --verbose SwiftLint.podspec docs: swift run swiftlint generate-docs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2bb78a9448..f9bfe1dc97 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -70,19 +70,6 @@ jobs: - script: swift build -c release --product SwiftLintBuildToolPlugin displayName: Build build tool plugin -- job: CocoaPods - pool: - vmImage: 'macOS-14' - variables: - DEVELOPER_DIR: /Applications/Xcode_16.1.app - steps: - - script: bundle install --path vendor/bundle - displayName: Install dependencies - - script: bundle exec pod repo update - displayName: Update CocoaPods repo - - script: bundle exec pod lib lint --platforms=macos --verbose - displayName: Lint Podspec for macOS - - job: Jazzy pool: vmImage: 'macOS-14' From b368c42f4fa9eec55cd4981276bc33bc6eec392b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 19 Jan 2025 14:21:51 +0100 Subject: [PATCH 237/260] Lint Markdown files in PRs (#5972) --- .github/workflows/lint.yml | 15 +++++++++++++-- .markdownlint.yml | 4 ++++ CHANGELOG.md | 11 ++++++----- CONTRIBUTING.md | 4 +++- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 .markdownlint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ce9d9f7c34..a81e33d204 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,11 +6,22 @@ on: - '*' jobs: - lint: - name: Lint Repository + lint-swift: + name: Lint Swift runs-on: ubuntu-24.04 # "Noble Numbat" container: swift:6.0-noble steps: - uses: actions/checkout@v4 - name: Lint run: swift run swiftlint --reporter github-actions-logging --strict 2> /dev/null + lint-markdown: + name: Lint Markdown + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Lint + uses: DavidAnson/markdownlint-cli2-action@v19 + with: + globs: | + CHANGELOG.md + CONTRIBUTING.md diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000000..1a9d37a1ed --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,4 @@ +MD013: + line_length: 120 +MD024: + siblings_only: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d976780c7..b17ceb3fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,7 +83,8 @@ [riley-williams](https://github.com/riley-williams) [#5958](https://github.com/realm/SwiftLint/issues/5958) -* Add `ib_segue_action` to default configuration of `type_contents_order` rule on the same level as `ib_action` to define and document a standard position. +* Add `ib_segue_action` to default configuration of `type_contents_order` rule on the same level as `ib_action` to + define and document a standard position. [SimplyDanny](https://github.com/SimplyDanny) [#5524](https://github.com/realm/SwiftLint/issues/5524) @@ -95,14 +96,16 @@ [SimplyDanny](https://github.com/SimplyDanny) * The artifact bundle name has changed. `SwiftLintBinary-macos.artifactbundle.zip` is now called - `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. + `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. [Bradley Mackey](https://github.com/bradleymackey) [#5514](https://github.com/realm/SwiftLint/issues/5514) * The `inert_defer` and `unused_capture_list` rules have completely been removed after being deprecated for 2 years. [SimplyDanny](https://github.com/SimplyDanny) -* SwiftLint now requires a Swift 5.10 or higher compiler to build. [The Swift Package Manager plugins](https://github.com/SimplyDanny/SwiftLintPlugins) continue to work with Swift 5.9. +* SwiftLint now requires a Swift 5.10 or higher compiler to build. + [The Swift Package Manager plugins](https://github.com/SimplyDanny/SwiftLintPlugins) continue to work with + Swift 5.9. [SimplyDanny](https://github.com/SimplyDanny) * The `private_unit_test` rule's deprecated `regex` configuration option has been removed after 2 years. @@ -8045,5 +8048,3 @@ This release has seen a phenomenal uptake in community contributions! ## 0.1.0: Fresh Out Of The Dryer First Version! - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51d771aaa8..5ad56f95e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,9 @@ Then you can use the full power of Xcode/LLDB/Instruments to develop and debug y 1. `git clone https://github.com/realm/SwiftLint.git` 1. `cd SwiftLint` 1. `swift build [-c release]` -1. Use the produced `swiftlint` binary from the command line, either by running `swift run [-c release] [swiftlint] [arguments]` or by invoking the binary directly at `.build/[release|debug]/swiftlint` +1. Use the produced `swiftlint` binary from the command line, either by running + `swift run [-c release] [swiftlint] [arguments]` or by invoking the binary directly at + `.build/[release|debug]/swiftlint`. 1. For debugging, attach LLDB: `lldb -- .build/[release|debug]/swiftlint [arguments]` ### Code Generation From c3e6bbbfe7b22b74a7dc00ef5bf1941152521b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 19 Jan 2025 17:39:16 +0100 Subject: [PATCH 238/260] Fix issues reported by ShellCheck --- .github/workflows/docker.yml | 28 +++++++++++++++++----------- .github/workflows/post-release.yml | 6 +++--- .github/workflows/release.yml | 15 +++++++++------ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 01a5096ad1..66762e21a1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,28 +25,34 @@ jobs: - name: Define variables on push to `main` if: github.event_name == 'push' run: | - echo "CHECKOUT_REF=main" >> $GITHUB_ENV - echo "DOCKER_TAG=latest" >> $GITHUB_ENV - echo "OUTPUT_TYPE=type=registry" >> $GITHUB_ENV + { + echo "CHECKOUT_REF=main" + echo "DOCKER_TAG=latest" + echo "OUTPUT_TYPE=type=registry" + } >> "$GITHUB_ENV" - name: Define variables on release event if: github.event_name == 'release' run: | - echo "CHECKOUT_REF=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - echo "DOCKER_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - echo "OUTPUT_TYPE=type=registry" >> $GITHUB_ENV + { + echo "CHECKOUT_REF=${{ github.event.release.tag_name }}" + echo "DOCKER_TAG=${{ github.event.release.tag_name }}" + echo "OUTPUT_TYPE=type=registry" + } >> "$GITHUB_ENV" - name: Define variables on workflow call if: github.event_name != 'push' && github.event_name != 'release' run: | - echo "CHECKOUT_REF=${{ inputs.ref }}" >> $GITHUB_ENV - echo "DOCKER_TAG=${{ inputs.tag }}" >> $GITHUB_ENV - echo "OUTPUT_TYPE=type=local,dest=artifacts" >> $GITHUB_ENV + { + echo "CHECKOUT_REF=${{ inputs.ref }}" + echo "DOCKER_TAG=${{ inputs.tag }}" + echo "OUTPUT_TYPE=type=local,dest=artifacts" + } >> "$GITHUB_ENV" - uses: actions/checkout@v4 with: ref: ${{ env.CHECKOUT_REF }} - name: Set lowercase repository name - run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> $GITHUB_ENV + run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> "$GITHUB_ENV" env: - REPOSITORY: '${{ github.repository }}' + REPOSITORY: ${{ github.repository }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to GitHub registry diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index d3a8178df2..2ae0d3ac9b 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -16,7 +16,7 @@ jobs: id: retrieve_author run: | AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') - echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + echo "name=${AUTHOR}" >> "$GITHUB_OUTPUT" - name: Deploy to CocoaPods run: make pod_publish env: @@ -32,7 +32,7 @@ jobs: ref: ${{ github.event.release.tag_name }} - name: Parse checksum id: parse_checksum - run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> $GITHUB_OUTPUT + run: echo "checksum=$(grep -o '[a-fA-F0-9]\{64\}' Package.swift)" >> "$GITHUB_OUTPUT" - name: Dispatch release of plugins package uses: peter-evans/repository-dispatch@v3 with: @@ -55,7 +55,7 @@ jobs: id: retrieve_author run: | AUTHOR=$(echo ${{ github.event.release.author.login }} | tr '[:lower:]' '[:upper:]') - echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + echo "name=${AUTHOR}" >> "$GITHUB_OUTPUT" - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 703a8d5c1d..41126e59c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,11 +29,14 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Create release branch - run: >- - git fetch origin ${{ env.RELEASE_BRANCH }} - && git checkout ${{ env.RELEASE_BRANCH }} - || git checkout -b ${{ env.RELEASE_BRANCH }} + - name: Checkout or create release branch + run: | + if git ls-remote --exit-code --heads origin ${{ env.RELEASE_BRANCH }}; then + git fetch origin ${{ env.RELEASE_BRANCH }}:${{ env.RELEASE_BRANCH }} + git checkout ${{ env.RELEASE_BRANCH }} + else + git checkout -b ${{ env.RELEASE_BRANCH }} + fi - name: Lint Podspec # Make sure Podspec still builds okay on CI with old release. run: | make pod_lint @@ -50,7 +53,7 @@ jobs: id: retrieve_author run: | AUTHOR=$(echo ${{ github.actor }} | tr '[:lower:]' '[:upper:]') - echo "name=${AUTHOR}" >> $GITHUB_OUTPUT + echo "name=${AUTHOR}" >> "$GITHUB_OUTPUT" - name: Configure Git author id: configure_git_author uses: Homebrew/actions/git-user-config@master From 854d3f2e4ce6f7f6eb5ec1eea76ff877021eaf33 Mon Sep 17 00:00:00 2001 From: John Szumski <784312+jszumski@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:43:31 -0500 Subject: [PATCH 239/260] Fix issue referencing `Tests` package from another Bazel workspace (#5977) Allow BuiltInRulesTests data to be empty so a workspace can load the Tests package from a release archive. --- CHANGELOG.md | 3 ++- Tests/BUILD | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17ceb3fe3..6c01a45ecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ ### Bug Fixes -* None. +* Fix issue referencing the Tests package from another Bazel workspace. + [jszumski](https://github.com/jszumski) ## 0.58.2: New Year’s Fresh Fold diff --git a/Tests/BUILD b/Tests/BUILD index b73aa578d0..a4ef8d31cb 100644 --- a/Tests/BUILD +++ b/Tests/BUILD @@ -104,6 +104,7 @@ swift_test( name = "BuiltInRulesTests", data = glob( ["BuiltInRulesTests/Resources/**"], + allow_empty = True, # Bazel doesn't support paths with spaces in them exclude = ["BuiltInRulesTests/Resources/FileNameNoSpaceRuleFixtures/**"], ), From b4eb2df5671f4ff872534a7e5eda2d7b4bba4d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 22 Jan 2025 21:47:11 +0100 Subject: [PATCH 240/260] Allow severity of `duplicate_imports` rule to be configurable (#5979) --- CHANGELOG.md | 4 ++++ .../Rules/Idiomatic/DuplicateImportsRule.swift | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c01a45ecb..e950356916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ * Fix issue referencing the Tests package from another Bazel workspace. [jszumski](https://github.com/jszumski) +* Allow severity of `duplicate_imports` rule to be configurable. + [SimplyDanny](https://github.com/SimplyDanny) + [#5978](https://github.com/realm/SwiftLint/issues/5978) + ## 0.58.2: New Year’s Fresh Fold ### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift index 69514185f5..b9b1bf33c7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift @@ -26,6 +26,7 @@ struct DuplicateImportsRule: SwiftSyntaxCorrectableRule { file.duplicateImportsViolationPositions().map { position in StyleViolation( ruleDescription: Self.description, + severity: configuration.severity, location: Location(file: file, position: position) ) } From 6cf862c655c1adbdcd062eb8a707a1ee0ee2f449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 23 Jan 2025 19:05:21 +0100 Subject: [PATCH 241/260] Move more build steps into Makefile (#5975) --- .azure/templates/run-make.yml | 16 ++++++++++++++++ .buildkite/pipeline.yml | 14 ++++++-------- Makefile | 23 ++++++++++++++++++----- azure-pipelines.yml | 20 ++++++++------------ 4 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 .azure/templates/run-make.yml diff --git a/.azure/templates/run-make.yml b/.azure/templates/run-make.yml new file mode 100644 index 0000000000..8861ec66d8 --- /dev/null +++ b/.azure/templates/run-make.yml @@ -0,0 +1,16 @@ +# Run the commands in the Makefile for the specified rule. + +parameters: + - name: rule + type: string + +steps: + - script: >- + awk ' + $0 ~ "${{ parameters.rule }}:" { in_rule = 1; next } + in_rule && /^\t/ { print $0 } + in_rule && !/^\t/ { in_rule = 0 } + ' Makefile | while IFS= read -r command; do + eval "$command" + done + displayName: Run `${{ parameters.rule }}` rule diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index e3c492c64d..4b14922990 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -4,23 +4,21 @@ steps: - echo "+++ Build" - bazel build :swiftlint - echo "+++ Test" - - bazel test --test_output=errors //Tests/... + - make bazel_test - label: "SwiftPM" commands: - echo "+++ Test" - - swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES + - make spm_test - label: "Danger" commands: - - echo "--- Install Bundler" + - echo "+++ Install Bundler" - gem install bundler -v 2.4.22 - - echo "--- Bundle Install" - - bundle install - - echo "+++ Run Danger" - - bundle exec danger --verbose + - echo "+++ Run OSS Scan" + - make oss_scan - label: "TSan Tests" commands: - echo "+++ Test" - - bazel test --test_output=errors --build_tests_only --features=tsan --test_timeout=1000 //Tests/... + - make bazel_test_tsan - label: "Sourcery" commands: - echo "+++ Run Sourcery" diff --git a/Makefile b/Makefile index f0b09ff520..2296621325 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,13 @@ test_tsan: swift build --build-tests $(TSAN_SWIFT_BUILD_FLAGS) DYLD_INSERT_LIBRARIES=$(TSAN_LIB) $(TSAN_XCTEST) $(TSAN_TEST_BUNDLE) +spm_build_plugins: + swift build -c release --product SwiftLintCommandPlugin + swift build -c release --product SwiftLintBuildToolPlugin + +spm_test: + swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES + write_xcodebuild_log: xcodebuild -scheme swiftlint clean build-for-testing -destination "platform=macOS" > xcodebuild.log @@ -147,6 +154,9 @@ package: $(SWIFTLINT_EXECUTABLE) bazel_test: bazel test --test_output=errors //Tests/... +bazel_test_tsan: + bazel test --test_output=errors --build_tests_only --features=tsan --test_timeout=1000 //Tests/... + bazel_release: $(SWIFTLINT_EXECUTABLE) bazel build :release mv -f bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 $(SWIFTLINT_EXECUTABLE) . @@ -167,17 +177,20 @@ display_compilation_time: formula_bump: brew update && brew bump-formula-pr --tag=$(shell git describe --tags) --revision=$(shell git rev-parse HEAD) swiftlint -pod_publish: +bundle_install: bundle install + +oss_scan: bundle_install + bundle exec danger --verbose + +pod_publish: bundle_install bundle exec pod trunk push SwiftLint.podspec -pod_lint: - bundle install +pod_lint: bundle_install bundle exec pod lib lint --verbose SwiftLint.podspec -docs: +docs: bundle_install swift run swiftlint generate-docs - bundle install bundle exec jazzy get_version: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f9bfe1dc97..81af9d715e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,8 +11,9 @@ jobs: vmImage: 'ubuntu-24.04' # "Noble Numbat" container: swift:6.0-noble steps: - - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES - displayName: Run tests + - template: .azure/templates/run-make.yml + parameters: + rule: spm_test - job: bazel_linux displayName: 'Bazel, Linux : Swift 6' @@ -47,7 +48,7 @@ jobs: variables: DEVELOPER_DIR: /Applications/Xcode_$(xcode).app steps: - - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES + - script: make spm_test displayName: Run tests - job: plugins_linux # Plugins shall be able to run on older Swift versions. @@ -65,10 +66,9 @@ jobs: image: swift:6.0-noble container: $[ variables['image'] ] steps: - - script: swift build -c release --product SwiftLintCommandPlugin - displayName: Build command plugin - - script: swift build -c release --product SwiftLintBuildToolPlugin - displayName: Build build tool plugin + - template: .azure/templates/run-make.yml + parameters: + rule: spm_build_plugins - job: Jazzy pool: @@ -76,11 +76,7 @@ jobs: variables: DEVELOPER_DIR: /Applications/Xcode_15.4.app steps: - - script: swift run swiftlint generate-docs - displayName: Generate documentation - - script: bundle install --path vendor/bundle - displayName: Install dependencies - - script: bundle exec jazzy + - script: make docs displayName: Run Jazzy - script: > if ruby -rjson -e "j = JSON.parse(File.read('docs/undocumented.json')); exit j['warnings'].length != 0"; then From 010028acfa53ae225014b70d20d4b3b9a7a967d2 Mon Sep 17 00:00:00 2001 From: Keith Bauer Date: Thu, 30 Jan 2025 04:33:32 +1300 Subject: [PATCH 242/260] Parallelize SwiftLintFile construction for a modest speedup (#5967) --- .../Configuration/Configuration+LintableFiles.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift index 30ea7be975..c324845ff0 100644 --- a/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+LintableFiles.swift @@ -20,7 +20,9 @@ extension Configuration { forceExclude: Bool, excludeBy: ExcludeBy) -> [SwiftLintFile] { lintablePaths(inPath: path, forceExclude: forceExclude, excludeBy: excludeBy) - .compactMap(SwiftLintFile.init(pathDeferringReading:)) + .parallelCompactMap { + SwiftLintFile(pathDeferringReading: $0) + } } /// Returns the paths for files that can be linted by SwiftLint in the specified parent path. From 0460a6c1c6e4bbf0d6ecfefd9150b740b047e0ad Mon Sep 17 00:00:00 2001 From: Keith Bauer Date: Thu, 30 Jan 2025 09:53:13 +1300 Subject: [PATCH 243/260] Avoid reading files before checking the cache (#5973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danny Mösch --- Source/SwiftLintFramework/Models/Linter.swift | 50 ++++++++------ Tests/FrameworkTests/EmptyFileTests.swift | 69 +++++++++++++++++++ 2 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 Tests/FrameworkTests/EmptyFileTests.swift diff --git a/Source/SwiftLintFramework/Models/Linter.swift b/Source/SwiftLintFramework/Models/Linter.swift index 68f111fdc9..a307597d69 100644 --- a/Source/SwiftLintFramework/Models/Linter.swift +++ b/Source/SwiftLintFramework/Models/Linter.swift @@ -66,29 +66,33 @@ private extension Rule { return superfluousDisableCommandViolations } - // As we need the configuration to get custom identifiers. - // swiftlint:disable:next function_parameter_count function_body_length - func lint(file: SwiftLintFile, - regions: [Region], - benchmark: Bool, - storage: RuleStorage, - superfluousDisableCommandRule: SuperfluousDisableCommandRule?, - compilerArguments: [String]) -> LintResult? { + func shouldRun(onFile file: SwiftLintFile) -> Bool { // We shouldn't lint if the current Swift version is not supported by the rule guard SwiftVersion.current >= Self.description.minSwiftVersion else { - return nil + return false } // Empty files shouldn't trigger violations if `shouldLintEmptyFiles` is `false` if file.isEmpty, !shouldLintEmptyFiles { - return nil + return false } if !(self is any SourceKitFreeRule) && file.sourcekitdFailed { warnSourceKitFailedOnce() - return nil + return false } + return true + } + + // As we need the configuration to get custom identifiers. + // swiftlint:disable:next function_parameter_count + func lint(file: SwiftLintFile, + regions: [Region], + benchmark: Bool, + storage: RuleStorage, + superfluousDisableCommandRule: SuperfluousDisableCommandRule?, + compilerArguments: [String]) -> LintResult { let ruleID = Self.identifier let violations: [StyleViolation] @@ -226,12 +230,7 @@ public struct Linter { self.configuration = configuration self.compilerArguments = compilerArguments - let fileIsEmpty = file.isEmpty let rules = configuration.rules.filter { rule in - if fileIsEmpty, !rule.shouldLintEmptyFiles { - return false - } - if compilerArguments.isEmpty { return !(rule is any AnalyzerRule) } @@ -307,11 +306,15 @@ public struct CollectedLinter { let superfluousDisableCommandRule = rules.first(where: { $0 is SuperfluousDisableCommandRule }) as? SuperfluousDisableCommandRule - let validationResults = rules.parallelCompactMap { - $0.lint(file: file, regions: regions, benchmark: benchmark, - storage: storage, - superfluousDisableCommandRule: superfluousDisableCommandRule, - compilerArguments: compilerArguments) + let validationResults: [LintResult] = rules.parallelCompactMap { + guard $0.shouldRun(onFile: file) else { + return nil + } + + return $0.lint(file: file, regions: regions, benchmark: benchmark, + storage: storage, + superfluousDisableCommandRule: superfluousDisableCommandRule, + compilerArguments: compilerArguments) } let undefinedSuperfluousCommandViolations = self.undefinedSuperfluousCommandViolations( regions: regions, configuration: configuration, @@ -378,7 +381,10 @@ public struct CollectedLinter { } var corrections = [Correction]() - for rule in rules.compactMap({ $0 as? any CorrectableRule }) { + for rule in rules where rule.shouldRun(onFile: file) { + guard let rule = rule as? any CorrectableRule else { + continue + } let newCorrections = rule.correct(file: file, using: storage, compilerArguments: compilerArguments) corrections += newCorrections if newCorrections.isNotEmpty, !file.isVirtual { diff --git a/Tests/FrameworkTests/EmptyFileTests.swift b/Tests/FrameworkTests/EmptyFileTests.swift new file mode 100644 index 0000000000..5ef43bc707 --- /dev/null +++ b/Tests/FrameworkTests/EmptyFileTests.swift @@ -0,0 +1,69 @@ +import SwiftLintFramework +import XCTest + +// swiftlint:disable:next balanced_xctest_lifecycle +final class EmptyFileTests: SwiftLintTestCase { + var collectedLinter: CollectedLinter! // swiftlint:disable:this implicitly_unwrapped_optional + var ruleStorage: RuleStorage! // swiftlint:disable:this implicitly_unwrapped_optional + + override func setUpWithError() throws { + let ruleList = RuleList(rules: RuleMock.self, RuleMock.self) + let configuration = try Configuration(dict: [:], ruleList: ruleList) + let file = SwiftLintFile(contents: "") + let linter = Linter(file: file, configuration: configuration) + ruleStorage = RuleStorage() + collectedLinter = linter.collect(into: ruleStorage) + } + + func testShouldLintEmptyFileRespectedDuringLint() throws { + let styleViolations = collectedLinter.styleViolations(using: ruleStorage) + XCTAssertEqual(styleViolations.count, 1) + XCTAssertEqual(styleViolations.first?.ruleIdentifier, "rule_mock") + } + + func testShouldLintEmptyFileRespectedDuringCorrect() throws { + let corrections = collectedLinter.correct(using: ruleStorage) + XCTAssertEqual(corrections.count, 1) + XCTAssertEqual(corrections.first?.ruleDescription.identifier, "rule_mock") + } +} + +private protocol ShouldLintEmptyFilesProtocol { + static var shouldLintEmptyFiles: Bool { get } +} + +private struct LintEmptyFiles: ShouldLintEmptyFilesProtocol { + static var shouldLintEmptyFiles: Bool { true } +} + +private struct DontLintEmptyFiles: ShouldLintEmptyFilesProtocol { + static var shouldLintEmptyFiles: Bool { false } +} + +private struct RuleMock: CorrectableRule { + var configuration = SeverityConfiguration(.warning) + + static var description: RuleDescription { + RuleDescription(identifier: "rule_mock<\(ShouldLintEmptyFiles.self)>", + name: "", + description: "", + kind: .style, + deprecatedAliases: ["mock"]) + } + + var shouldLintEmptyFiles: Bool { + ShouldLintEmptyFiles.shouldLintEmptyFiles + } + + func validate(file: SwiftLintFile) -> [StyleViolation] { + [ + StyleViolation(ruleDescription: Self.description, location: Location(file: file.path)) + ] + } + + func correct(file: SwiftLintFile) -> [Correction] { + [ + Correction(ruleDescription: Self.description, location: Location(file: file.path)) + ] + } +} From 6ba231a11ad6b9d4763277a1dd463d5390a6144f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Thu, 30 Jan 2025 22:50:18 +0100 Subject: [PATCH 244/260] Consider composed inherited types (#5984) --- CHANGELOG.md | 4 +++ .../Lint/ClassDelegateProtocolRule.swift | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e950356916..8f18951406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5978](https://github.com/realm/SwiftLint/issues/5978) +* Consider types restricting a protocol to classes in composed inherited types in `class_delegate_protocol` rule. + [SimplyDanny](https://github.com/SimplyDanny) + [#5982](https://github.com/realm/SwiftLint/issues/5982) + ## 0.58.2: New Year’s Fresh Fold ### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/ClassDelegateProtocolRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/ClassDelegateProtocolRule.swift index ce556449b9..ef41645d91 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/ClassDelegateProtocolRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/ClassDelegateProtocolRule.swift @@ -18,15 +18,22 @@ struct ClassDelegateProtocolRule: Rule { Example("@objc(MyFooDelegate)\n protocol FooDelegate {}"), Example("protocol FooDelegate: BarDelegate {}"), Example("protocol FooDelegate: AnyObject {}"), + Example("protocol FooDelegate: AnyObject & Foo {}"), + Example("protocol FooDelegate: Foo, AnyObject & Foo {}"), + Example("protocol FooDelegate: Foo & AnyObject & Bar {}"), Example("protocol FooDelegate: NSObjectProtocol {}"), Example("protocol FooDelegate where Self: BarDelegate {}"), + Example("protocol FooDelegate where Self: BarDelegate & Bar {}"), + Example("protocol FooDelegate where Self: Foo & BarDelegate & Bar {}"), Example("protocol FooDelegate where Self: AnyObject {}"), Example("protocol FooDelegate where Self: NSObjectProtocol {}"), ], triggeringExamples: [ Example("↓protocol FooDelegate {}"), Example("↓protocol FooDelegate: Bar {}"), + Example("↓protocol FooDelegate: Foo & Bar {}"), Example("↓protocol FooDelegate where Self: StringProtocol {}"), + Example("↓protocol FooDelegate where Self: A & B {}"), ] ) } @@ -38,10 +45,10 @@ private extension ClassDelegateProtocolRule { } override func visitPost(_ node: ProtocolDeclSyntax) { - if node.name.text.hasSuffix("Delegate") && - !node.hasObjCAttribute() && - !node.isClassRestricted() && - !node.inheritsFromObjectOrDelegate() { + if node.name.text.hasSuffix("Delegate"), + !node.hasObjCAttribute(), + !node.isClassRestricted(), + !node.inheritsFromObjectOrDelegate() { violations.append(node.protocolKeyword.positionAfterSkippingLeadingTrivia) } } @@ -81,10 +88,12 @@ private extension ProtocolDeclSyntax { private extension TypeSyntax { func isObjectOrDelegate() -> Bool { - guard let typeName = self.as(IdentifierTypeSyntax.self)?.typeName else { - return false + if let typeName = `as`(IdentifierTypeSyntax.self)?.typeName { + return typeName == "AnyObject" || typeName == "NSObjectProtocol" || typeName.hasSuffix("Delegate") } - - return typeName == "AnyObject" || typeName == "NSObjectProtocol" || typeName.hasSuffix("Delegate") + if let combined = `as`(CompositionTypeSyntax.self) { + return combined.elements.contains { $0.type.isObjectOrDelegate() } + } + return false } } From a4893f60b2fdb10db0f58dcd06e270adc876125d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Fri, 31 Jan 2025 20:01:51 +0100 Subject: [PATCH 245/260] Verify that severity can be configured per rule (#5985) --- Tests/TestHelpers/TestHelpers.swift | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Tests/TestHelpers/TestHelpers.swift b/Tests/TestHelpers/TestHelpers.swift index 9c4551d5d8..e39010931c 100644 --- a/Tests/TestHelpers/TestHelpers.swift +++ b/Tests/TestHelpers/TestHelpers.swift @@ -357,6 +357,7 @@ public extension XCTestCase { testMultiByteOffsets: testMultiByteOffsets) } + // swiftlint:disable:next function_body_length func verifyLint(_ ruleDescription: RuleDescription, config: Configuration, commentDoesntViolate: Bool = true, @@ -376,8 +377,11 @@ public extension XCTestCase { violations(example, config: config, requiresFileOnDisk: ruleDescription.requiresFileOnDisk) } - let ruleDescription = ruleDescription.focused() - let (triggers, nonTriggers) = (ruleDescription.triggeringExamples, ruleDescription.nonTriggeringExamples) + let focusedRuleDescription = ruleDescription.focused() + let (triggers, nonTriggers) = ( + focusedRuleDescription.triggeringExamples, + focusedRuleDescription.nonTriggeringExamples + ) verify(triggers: triggers, nonTriggers: nonTriggers) if testMultiByteOffsets { @@ -432,6 +436,27 @@ public extension XCTestCase { line: trigger.line) } } + + // Severity can be changed + let ruleType = RuleRegistry.shared.rule(forID: ruleDescription.identifier) + if ruleType?.init().configuration is (any SeverityBasedRuleConfiguration), + let example = triggers.first(where: { $0.configuration == nil }) { + let withWarning = Example(example.code, configuration: ["severity": "warning"]) + XCTAssert( + violations(withWarning, config: config).allSatisfy { $0.severity == .warning }, + "Violation severity cannot be changed to warning", + file: example.file, + line: 1 + ) + + let withError = Example(example.code, configuration: ["severity": "error"]) + XCTAssert( + violations(withError, config: config).allSatisfy { $0.severity == .error }, + "Violation severity cannot be changed to error", + file: example.file, + line: 1 + ) + } } func verifyCorrections(_ ruleDescription: RuleDescription, From 4eb19bef173b6b7041a0836a8b5131dd89d05465 Mon Sep 17 00:00:00 2001 From: Keith Bauer Date: Sun, 2 Feb 2025 01:10:44 +1300 Subject: [PATCH 246/260] Parallelize file grouping (#5983) --- .../Extensions/Array+SwiftLint.swift | 40 +++++++++++++++++++ .../Configuration+CommandLine.swift | 9 +---- .../Configuration+RulesWrapper.swift | 10 ++++- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift index 89eda406f6..0f67ddae40 100644 --- a/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/Array+SwiftLint.swift @@ -62,6 +62,46 @@ public extension Array { Dictionary(grouping: self, by: { transform($0) }) } + /// Group the elements in this array into a dictionary, keyed by applying the specified `transform`. + /// Elements for which the `transform` returns a `nil` key are removed. + /// + /// - parameter transform: The transformation function to extract an element to its group key, + /// or exclude the element. + /// + /// - returns: The elements grouped by applying the specified transformation. + func filterGroup(by transform: (Element) -> U?) -> + [U: [Element]] where Element: Sendable { + var result = [U: [Element]]() + for element in self { + if let key = transform(element) { + result[key, default: []].append(element) + } + } + return result + } + + /// Same as `filterGroup`, but spreads the work in the `transform` block in parallel using GCD's + /// `concurrentPerform`. + /// + /// - parameter transform: The transformation function to extract an element to its group key, + /// or exclude the element. + /// + /// - returns: The elements grouped by applying the specified transformation. + func parallelFilterGroup(by transform: @Sendable (Element) -> U?) -> + [U: [Element]] where Element: Sendable { + if count < 16 { + return filterGroup(by: transform) + } + let pivot = count / 2 + let results = [ + Array(self[0..) -> String { diff --git a/Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift b/Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift index 6e3295d951..2d883ee532 100644 --- a/Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift +++ b/Source/SwiftLintFramework/Configuration/Configuration+RulesWrapper.swift @@ -4,6 +4,7 @@ internal extension Configuration { class RulesWrapper { // MARK: - Properties private static var isOptInRuleCache: [String: Bool] = [:] + private static let isOptInRuleCacheLock = NSLock() let allRulesWrapped: [ConfigurationRuleWrapper] internal let mode: RulesMode @@ -293,13 +294,18 @@ internal extension Configuration { private func isOptInRule( _ identifier: String, allRulesWrapped: [ConfigurationRuleWrapper] ) -> Bool { - if let cachedIsOptInRule = Self.isOptInRuleCache[identifier] { + let cachedIsOptInRule = Self.isOptInRuleCacheLock.withLock { + Self.isOptInRuleCache[identifier] + } + if let cachedIsOptInRule { return cachedIsOptInRule } let isOptInRule = allRulesWrapped .first { type(of: $0.rule).identifier == identifier }?.rule is any OptInRule - Self.isOptInRuleCache[identifier] = isOptInRule + Self.isOptInRuleCacheLock.withLock { + Self.isOptInRuleCache[identifier] = isOptInRule + } return isOptInRule } } From f4ccdc4863f06cd0d1542d3af18cd9f577a296dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 2 Feb 2025 21:04:17 +0100 Subject: [PATCH 247/260] Automatically merge release branch upon publishing a release --- .github/workflows/post-release.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 2ae0d3ac9b..b0698f81bb 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -5,8 +5,21 @@ on: types: published jobs: + merge-into-main: + name: Merge into main + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: main + - name: Merge release branch + run: | + git fetch origin release/${{ github.event.release.tag_name }} + git merge --ff-only release/${{ github.event.release.tag_name }} + git push origin main publish-pod: name: Publish Pod + needs: merge-into-main runs-on: macOS-14 steps: - uses: actions/checkout@v4 @@ -24,6 +37,7 @@ jobs: COCOAPODS_TRUNK_TOKEN: ${{ secrets[format('COCOAPODS_TRUNK_TOKEN_{0}', steps.retrieve_author.outputs.name)] }} dispatch-plugins: name: Dispatch Plugins Repository + needs: merge-into-main runs-on: ubuntu-24.04 steps: - name: Checkout repository @@ -47,6 +61,7 @@ jobs: } bump-homebrew: name: Bump Homebrew Formula + needs: merge-into-main runs-on: ubuntu-24.04 container: image: ghcr.io/homebrew/ubuntu24.04:latest From 2ba03c17b9f92069935b0a53921a726459edcdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 4 Feb 2025 10:33:23 +0100 Subject: [PATCH 248/260] Add job to lint workflow files (#5989) --- .github/workflows/lint.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a81e33d204..211745f52d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,3 +25,16 @@ jobs: globs: | CHANGELOG.md CONTRIBUTING.md + lint-actions: + name: Lint Actions + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Register problem matcher + run: | + curl -sSL https://raw.githubusercontent.com/rhysd/actionlint/main/.github/actionlint-matcher.json > actionlint-matcher.json + echo "::add-matcher::actionlint-matcher.json" + - name: Lint + uses: docker://rhysd/actionlint:latest + with: + args: -color From b2312b50b6942eee2b04cfbc66e0913c604c60d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 8 Feb 2025 21:20:46 +0100 Subject: [PATCH 249/260] Update Docker image used in testing --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2296621325..245620ca11 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ docker_image: docker build --platform linux/amd64 --force-rm --tag swiftlint . docker_test: - docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm swift:5.9-focal swift test --parallel + docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm swift:6.0-noble swift test --parallel docker_htop: docker run --platform linux/amd64 -it --rm --pid=container:swiftlint terencewestphal/htop || reset From 2686268a9164d550c320767322bb9adb984a8296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 8 Feb 2025 21:25:56 +0100 Subject: [PATCH 250/260] Fix linting issues in README (#5991) --- .github/workflows/lint.yml | 1 + .markdownlint.yml | 5 +++++ README.md | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 211745f52d..159a54e6ff 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,6 +25,7 @@ jobs: globs: | CHANGELOG.md CONTRIBUTING.md + README.md lint-actions: name: Lint Actions runs-on: ubuntu-24.04 diff --git a/.markdownlint.yml b/.markdownlint.yml index 1a9d37a1ed..d2a629c023 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,4 +1,9 @@ MD013: line_length: 120 + code_blocks: false MD024: siblings_only: true +MD033: + allowed_elements: + - details + - summary diff --git a/README.md b/README.md index 282de73915..73ec013550 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ of your source files for more accurate results. [![Azure Build Status](https://dev.azure.com/jpsim/SwiftLint/_apis/build/status/realm.SwiftLint?branchName=main)](https://dev.azure.com/jpsim/SwiftLint/_build/latest?definitionId=4?branchName=main) [![Buildkite Build Status](https://badge.buildkite.com/e2a5bc32c347e76e2793e4c5764a5f42bcd42bbe32f79c3a53.svg?branch=main)](https://buildkite.com/swiftlint/swiftlint) -![](https://raw.githubusercontent.com/realm/SwiftLint/main/assets/screenshot.png) +![SwiftLint violations highlighted in the Xcode editor](https://raw.githubusercontent.com/realm/SwiftLint/main/assets/screenshot.png) This project adheres to the [Contributor Covenant Code of Conduct](https://realm.io/conduct). @@ -353,7 +353,7 @@ fi > The `SWIFTLINT_CMD` path uses the default Xcode configuration and has been > tested on Xcode 15/16. In case of another configuration (e.g. a custom > Swift package path), please adapt the values accordingly. - + > [!TIP] > Uncheck `Based on dependency analysis` to run `swiftlint` on all incremental > builds, suppressing the unspecified outputs warning. @@ -817,7 +817,7 @@ custom_rules: This is what the output would look like: -![](https://raw.githubusercontent.com/realm/SwiftLint/main/assets/custom-rule.png) +![Custom violations highlighted in the Xcode editor](https://raw.githubusercontent.com/realm/SwiftLint/main/assets/custom-rule.png) It is important to note that the regular expression pattern is used with the flags `s` and `m` enabled, that is `.` @@ -1041,7 +1041,7 @@ and helping others in the community. Special thanks go to [MacStadium](https://www.macstadium.com) for providing physical Mac mini machines to run our performance tests. - +![MacStadium](https://raw.githubusercontent.com/realm/SwiftLint/main/assets/macstadium.png) We also thank Realm (now MongoDB) for their inital contributions and setup of the project. From b0e4fc528224281dc2e132ac63c72c6d40ccc17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sat, 8 Feb 2025 21:27:08 +0100 Subject: [PATCH 251/260] Update contribution manual (#5992) --- CONTRIBUTING.md | 154 +++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 66 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ad56f95e0..cc38027ced 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,31 +2,30 @@ ## Tutorial -If you'd like to write a SwiftLint rule but aren't sure how to start, -please watch and follow along with -[this video tutorial](https://vimeo.com/819268038). +If you'd like to write a SwiftLint rule but aren't sure how to start, please watch and follow along with [this video +tutorial](https://vimeo.com/819268038). ## Pull Requests -All changes, no matter how trivial, must be done via pull requests. Commits -should never be made directly on the `main` branch. If possible, avoid mixing -different aspects in one pull request. Prefer squashing if there are commits -that are not reasonable alone. To update your PR branch and resolve conflicts, -prefer rebasing over merging `main`. +All changes, no matter how trivial, must be done via pull requests. Commits should never be made directly on the `main` +branch. If possible, avoid mixing different aspects in one pull request. Prefer squashing if there are commits that are +not reasonable alone. To update your PR branch and resolve conflicts, use rebasing instead of merging `main`. -_If you have commit access to SwiftLint and believe your change to be trivial -and not worth waiting for review, you may open a pull request and merge it -immediately, but this should be the exception, not the norm._ +> [!IMPORTANT] If you have commit access to SwiftLint and believe your change to be trivial and not worth waiting for +> review, you may open a pull request and merge it immediately, but this should be the exception, not the norm. ## Building and Running Locally +The first step is to clone the repository. We recommend Xcode or Visual Studio Code with the +[awesome Swift extension](https://github.com/swiftlang/vscode-swift) installed from the +[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=swiftlang.swift-vscode) or the +[Open VSX Registry](https://open-vsx.org/extension/sswg/swift-lang) for development. + ### Using Xcode -1. `git clone https://github.com/realm/SwiftLint.git` -1. `cd SwiftLint` 1. `xed .` 1. Select the "swiftlint" scheme. -1. `cmd-opt-r` to open the scheme options. +1. Press `⌘ Cmd` `⌥ Option` `R` to open the scheme options. 1. Set the "Arguments Passed On Launch" you want in the "Arguments" tab. See available arguments [in the README](https://github.com/realm/SwiftLint#command-line). 1. Set the "Working Directory" in the "Options" tab to the path where you would like @@ -39,28 +38,36 @@ to execute SwiftLint — a folder that contains Swift source files. Then you can use the full power of Xcode/LLDB/Instruments to develop and debug your changes to SwiftLint. +### Using Visual Studio Code + +1. `code .` +1. Wait for the setup to complete. +1. `⌘ Cmd` `⇧ Shift` `P` to open the command palette. +1. With the [Swift extension](https://github.com/swiftlang/vscode-swift) installed search for and select + "Task: Run Build Task". +1. Decide to build the `swiftlint` binary only or to build everything including tests. +1. The extension allows to to debug the binary and run tests. + ### Using the Command Line -1. `git clone https://github.com/realm/SwiftLint.git` -1. `cd SwiftLint` 1. `swift build [-c release]` 1. Use the produced `swiftlint` binary from the command line, either by running `swift run [-c release] [swiftlint] [arguments]` or by invoking the binary directly at `.build/[release|debug]/swiftlint`. -1. For debugging, attach LLDB: `lldb -- .build/[release|debug]/swiftlint [arguments]` +1. For debugging, attach LLDB: `lldb -- .build/[release|debug]/swiftlint [arguments]`. ### Code Generation -If XCTest cases or functions are added/removed/renamed, or if rules are -added/removed/renamed, you'll need to run `make sourcery`, which requires that -[Sourcery](https://github.com/krzysztofzablocki/Sourcery) be installed on your -machine. This will update source files to reflect these changes. +If rules are added/removed/renamed, you'll need to run `make sourcery`, which requires that [Bazel](https://bazel.build) +is installed on your machine (`brew install bazelisk`). This will update source files to reflect these changes. + +If you'd like to avoid installing Bazel, you can run Sourcery manually. Make sure to use the same version of Sourcery as +defined in [WORKSPACE](WORKSPACE). ### Tests -SwiftLint supports building via Xcode and Swift Package Manager on macOS, and -with Swift Package Manager on Linux. When contributing code changes, please -ensure that all four supported build methods continue to work and pass tests: +SwiftLint supports building via Xcode and Swift Package Manager on macOS, and with Swift Package Manager on Linux. When +contributing code changes, please ensure that all four supported build methods continue to work and pass tests: ```shell xcodebuild -scheme swiftlint test -destination 'platform=macOS' @@ -69,29 +76,26 @@ make bazel_test make docker_test ``` +In case you consider it too much effort to installed all the tooling required for the different build/test methods, just +open a pull request and watch the CI results carefully. They include all the necessary builds and checks. + ## Rules New rules should be added in the `Source/SwiftLintBuiltInRules/Rules` directory. -Prefer implementing new rules with the help of SwiftSyntax. Look for the -`@SwiftSyntaxRule` attribute for examples and use the same on your own rule. -New rules should conform to either `Rule` or `OptInRule` depending on whether -they shall be enabled by default or opt-in, respectively. +Prefer implementing new rules with the help of SwiftSyntax. Look for the `@SwiftSyntaxRule` attribute for examples and +use the same on your own rule. All new rules or changes to existing rules should be accompanied by unit tests. -Whenever possible, prefer adding tests via the `triggeringExamples` and -`nonTriggeringExamples` properties of a rule's `description` rather than adding -those test cases in the unit tests directly. This makes it easier to understand -what rules do by reading their source, and simplifies adding more test cases -over time. With `make sourcery`, you ensure that all test cases are automatically -checked in unit tests. Moreover, the examples added to a rule will appear in the -rule's rendered documentation accessible from the -[Rule Directory](https://realm.github.io/SwiftLint/rule-directory.html). +Whenever possible, prefer adding tests via the `triggeringExamples` and `nonTriggeringExamples` properties of a rule's +`description` rather than adding those test cases in unit tests directly. This makes it easier to understand what rules +do by reading their source, and simplifies adding more test cases over time. With `make sourcery`, you ensure that all +test cases are automatically checked in unit tests. Moreover, the examples added to a rule will appear in the rule's +rendered documentation accessible from the [Rule Directory](https://realm.github.io/SwiftLint/rule-directory.html). -For debugging purposes, examples can be marked as `focused`. If there are any -focused examples found, then only those will be run when running all tests for that -rule. +For debugging purposes, examples can be marked as `focused`. If there are any focused examples found, then only those +will be run when executing all tests for that rule. ```swift nonTriggeringExamples: [ @@ -106,16 +110,15 @@ triggeringExamples: [ ### Configuration -Every rule is configurable via `.swiftlint.yml`, even if only by settings its default -severity. This is done by setting the `configuration` property of a rule as: +Every rule is configurable via `.swiftlint.yml`, even if only by settings its default severity. This is done by setting +the `configuration` property of a rule as: ```swift var configuration = SeverityConfiguration(.warning) ``` -If a rule requires more options, a specific configuration can be implemented -and associated with the rule via its `configuration` property. Check for rules providing -their own configurations as extensive examples or check out +If a rule requires more options, a specific configuration can be implemented and associated with the rule via its +`configuration` property. Check for rules providing their own configurations as extensive examples or check out * [`ForceCastRule`](https://github.com/realm/SwiftLint/blob/main/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ForceCastRule.swift) for a rule that allows severity configuration, @@ -145,23 +148,30 @@ identifier_name: All changes should be made via pull requests on GitHub. -When issuing a pull request with user-facing changes, please add a -summary of your changes to the `CHANGELOG.md` file. +When issuing a pull request with user-facing changes, please add a summary of your changes to the `CHANGELOG.md` file. We follow the same syntax as CocoaPods' CHANGELOG.md: 1. One Markdown unnumbered list item describing the change. 1. 2 trailing spaces on the last line describing the change (so that Markdown renders each change - [on its own line](https://daringfireball.net/projects/markdown/syntax#p)). -1. A list of Markdown hyperlinks to the contributors to the change. One entry - per line. Usually just one. -1. A list of Markdown hyperlinks to the issues the change addresses. One entry - per line. Usually just one. If there was no issue tracking this change, - you may instead link to the change's pull request. -1. All CHANGELOG.md content is hard-wrapped at 80 characters. + [on its own line](https://daringfireball.net/projects/markdown/syntax#p)). +1. A list of Markdown hyperlinks to the contributors to the change. Usually just one. +1. A list of Markdown hyperlinks to the issues the change addresses. Usually just one or even none. Mentioning the pull + request number is not necessary, as GitHub automatically adds it to the commit message upon squash-merge. +1. All `CHANGELOG.md` content is hard-wrapped at 80 characters. ## Cutting a Release +The release workflows require two tokens specific to your GitHub user account to be set as Action secrets in the +SwiftLint repository. Make sure you have the following steps completed once before cutting your first release: + +1. Navigate to [Action secrets and variables](https://github.com/realm/SwiftLint/settings/secrets/actions) in the + repository settings. +1. Add a new secret named `PERSONAL_GITHUB_TOKEN_` where `` is your GitHub username. The value must + be a personal access token with the `read:user`, `repo`, `user:email` and `workflow` scopes. +1. Follow [these instructions](https://medium.com/swlh/automated-cocoapod-releases-with-github-actions-8526dd4535c7) to + set the variable `COCOAPODS_TRUNK_TOKEN_` to your CocoaPods trunk token. + SwiftLint maintainers follow these steps to cut a release: 1. Come up with a witty washer- or dryer-themed release name. Past names include: @@ -169,32 +179,44 @@ SwiftLint maintainers follow these steps to cut a release: * FabricSoftenerRule * Top Loading * Fresh Out Of The Dryer + You may ask your favorite AI chat bot for suggestions. :robot: +1. In the [GitHub UI](https://github.com/realm/SwiftLint/actions/workflows/release.yml) press "Run workflow". Enter the + new release version and the title. Start the workflow and wait for it to **create a release branch**, + **build the most important artifacts** and **create a draft release**. +1. Review the draft release making sure that the artifacts have been attached to it and the release notes are correct. +1. If everything looks good and the **release branch has not diverged from `main`**, publish the release. :rocket: +1. A few "post-release" jobs will get started completing the list of artifacts on the release page. It will also + fast-forward merge the release branch into `main`. +1. Celebrate. :tada: + +In case the CocoaPods release fails, you can try to publish it manually: + 1. Make sure you have the latest stable Xcode version installed and `xcode-select`ed. 1. Make sure that the selected Xcode has the latest SDKs of all supported platforms installed. This is required to build the CocoaPods release. -1. Release a new version by running `make release "0.2.0: Tumble Dry"`. -1. Celebrate. :tada: +1. Run `make pod_publish`. ## CI -SwiftLint uses Azure Pipelines for most of its CI jobs, primarily because -they're the only CI provider to have a free tier with 10x concurrency on macOS. +SwiftLint uses Azure Pipelines for most of its CI jobs, primarily because they're the only CI provider to have a free +tier with 10x concurrency on macOS. -Some CI jobs run in GitHub Actions (e.g. Docker). +Some CI jobs run as GitHub Actions (e.g. Docker build, linting, release workflows). -Some CI jobs run on Buildkite using Mac Minis from MacStadium. These are jobs -that benefit from being run on the latest Xcode & macOS versions on bare metal. +The most important CI jobs run on Buildkite using Macs provided by MacStadium. These are jobs that benefit from being +run on the latest Xcode & macOS versions on bare metal. This is important for performance comparisons and caching in +Bazel builds. ### Buildkite Setup To bring up a new Buildkite worker from MacStadium: -1. Change account password -1. Update macOS to the latest version -1. Install Homebrew: -1. Install Buildkite agent and other tools via Homebrew: +1. Change account password. +1. Update macOS to the latest version. +1. [Install Homebrew](https://brew.sh). +1. Install the Buildkite agent and other tools via Homebrew: `brew install aria2 bazelisk htop buildkite/buildkite/buildkite-agent robotsandpencils/made/xcodes` 1. Install latest Xcode version: `xcodes update && xcodes install 14.0.0` 1. Add `DANGER_GITHUB_API_TOKEN` and `HOME` to `/opt/homebrew/etc/buildkite-agent/hooks/environment` -1. Configure and launch buildkite agent: `brew info buildkite-agent` / - +1. Configure and launch buildkite agent as described in `brew info buildkite-agent` or on + . From 23de3822049cdaac2479378f22c151c6464bacf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 9 Feb 2025 22:45:51 +0100 Subject: [PATCH 252/260] Fix some typos (#5993) --- CONTRIBUTING.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc38027ced..2fe766a55f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,8 @@ All changes, no matter how trivial, must be done via pull requests. Commits shou branch. If possible, avoid mixing different aspects in one pull request. Prefer squashing if there are commits that are not reasonable alone. To update your PR branch and resolve conflicts, use rebasing instead of merging `main`. -> [!IMPORTANT] If you have commit access to SwiftLint and believe your change to be trivial and not worth waiting for +> [!IMPORTANT] +> If you have commit access to SwiftLint and believe your change to be trivial and not worth waiting for > review, you may open a pull request and merge it immediately, but this should be the exception, not the norm. ## Building and Running Locally @@ -42,11 +43,11 @@ Then you can use the full power of Xcode/LLDB/Instruments to develop and debug y 1. `code .` 1. Wait for the setup to complete. -1. `⌘ Cmd` `⇧ Shift` `P` to open the command palette. +1. Press `⌘ Cmd` `⇧ Shift` `P` to open the command palette. 1. With the [Swift extension](https://github.com/swiftlang/vscode-swift) installed search for and select "Task: Run Build Task". 1. Decide to build the `swiftlint` binary only or to build everything including tests. -1. The extension allows to to debug the binary and run tests. +1. The extension allows you to debug the binary and run tests. ### Using the Command Line @@ -61,8 +62,8 @@ Then you can use the full power of Xcode/LLDB/Instruments to develop and debug y If rules are added/removed/renamed, you'll need to run `make sourcery`, which requires that [Bazel](https://bazel.build) is installed on your machine (`brew install bazelisk`). This will update source files to reflect these changes. -If you'd like to avoid installing Bazel, you can run Sourcery manually. Make sure to use the same version of Sourcery as -defined in [WORKSPACE](WORKSPACE). +If you'd rather like to avoid installing Bazel, you can run Sourcery manually. Make sure to use the same version of +Sourcery as defined in [WORKSPACE](WORKSPACE). ### Tests @@ -76,7 +77,7 @@ make bazel_test make docker_test ``` -In case you consider it too much effort to installed all the tooling required for the different build/test methods, just +If you find it too much effort to installed all the tooling required for the different build/test methods, just open a pull request and watch the CI results carefully. They include all the necessary builds and checks. ## Rules @@ -167,8 +168,9 @@ SwiftLint repository. Make sure you have the following steps completed once befo 1. Navigate to [Action secrets and variables](https://github.com/realm/SwiftLint/settings/secrets/actions) in the repository settings. -1. Add a new secret named `PERSONAL_GITHUB_TOKEN_` where `` is your GitHub username. The value must - be a personal access token with the `read:user`, `repo`, `user:email` and `workflow` scopes. +1. Add a new secret named `PERSONAL_GITHUB_TOKEN_` where `` is your GitHub username in all + uppercase. The value must be a personal access token with the `read:user`, `repo`, `user:email` and + `workflow` scopes. 1. Follow [these instructions](https://medium.com/swlh/automated-cocoapod-releases-with-github-actions-8526dd4535c7) to set the variable `COCOAPODS_TRUNK_TOKEN_` to your CocoaPods trunk token. @@ -179,15 +181,18 @@ SwiftLint maintainers follow these steps to cut a release: * FabricSoftenerRule * Top Loading * Fresh Out Of The Dryer + You may ask your favorite AI chat bot for suggestions. :robot: 1. In the [GitHub UI](https://github.com/realm/SwiftLint/actions/workflows/release.yml) press "Run workflow". Enter the - new release version and the title. Start the workflow and wait for it to **create a release branch**, - **build the most important artifacts** and **create a draft release**. -1. Review the draft release making sure that the artifacts have been attached to it and the release notes are correct. -1. If everything looks good and the **release branch has not diverged from `main`**, publish the release. :rocket: -1. A few "post-release" jobs will get started completing the list of artifacts on the release page. It will also - fast-forward merge the release branch into `main`. -1. Celebrate. :tada: + release version and the title. Start the workflow and wait for it to **create a release branch**, + **build the most important artifacts** and **prepare a draft release**. +1. Review the draft release thoroughly making sure that the artifacts have been attached to it and the release notes are + correct. +1. If everything looks good and the **release branch has not diverged from `main`** in the meantime, publish the + release. :rocket: +1. A few "post-release" jobs will get started to complete the list of artifacts on the release page. One of them + will also fast-forward merge the release branch into `main`. All jobs fail if that's not possible. +1. Celebrate! :tada: In case the CocoaPods release fails, you can try to publish it manually: From 849dbfdd282f59ee9f3b22d96757b9970b4274ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 10 Feb 2025 19:23:42 +0100 Subject: [PATCH 253/260] Enable upcoming features improving concurrency checking (#5994) * `InferSendableFromCaptures` * `GlobalActorIsolatedTypesUsability` --- Package.swift | 6 ++++-- Tests/ExtraRulesTests/ExtraRulesTests.swift | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index e0447c706b..57ab9dcf70 100644 --- a/Package.swift +++ b/Package.swift @@ -3,11 +3,13 @@ import CompilerPluginSupport import PackageDescription let swiftFeatures: [SwiftSetting] = [ - .enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("ConciseMagicFile"), - .enableUpcomingFeature("ImportObjcForwardDeclarations"), + .enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("ForwardTrailingClosures"), + .enableUpcomingFeature("GlobalActorIsolatedTypesUsability"), .enableUpcomingFeature("ImplicitOpenExistentials"), + .enableUpcomingFeature("ImportObjcForwardDeclarations"), + .enableUpcomingFeature("InferSendableFromCaptures"), ] let strictConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency=complete")] let targetedConcurrency = [SwiftSetting.enableExperimentalFeature("StrictConcurrency=targeted")] diff --git a/Tests/ExtraRulesTests/ExtraRulesTests.swift b/Tests/ExtraRulesTests/ExtraRulesTests.swift index ee63d3f8e6..6f063f8fc1 100644 --- a/Tests/ExtraRulesTests/ExtraRulesTests.swift +++ b/Tests/ExtraRulesTests/ExtraRulesTests.swift @@ -1,7 +1,7 @@ @testable import SwiftLintExtraRules import TestHelpers -final class ExtraRulesTests: SwiftLintTestCase { +final class ExtraRulesTests: SwiftLintTestCase, @unchecked Sendable { func testWithDefaultConfiguration() { for ruleType in extraRules() { verifyRule(ruleType.description) From 82fd3e405814f960947dfefd4ee4f0671f950751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 10 Feb 2025 22:59:43 +0100 Subject: [PATCH 254/260] Ensure that content is complete using an awaitable stream (#5986) --- Source/SwiftLintCore/Models/Issue.swift | 27 +++--------- ...licitTypeInterfaceConfigurationTests.swift | 11 ++--- .../IndentationWidthRuleTests.swift | 17 ++++---- ...ultilineParametersConfigurationTests.swift | 30 +++++++------ .../NoEmptyBlockConfigurationTests.swift | 11 ++--- .../RuleConfigurationDescriptionTests.swift | 43 ++++++++++--------- 6 files changed, 66 insertions(+), 73 deletions(-) diff --git a/Source/SwiftLintCore/Models/Issue.swift b/Source/SwiftLintCore/Models/Issue.swift index 830911c8a9..b16417b139 100644 --- a/Source/SwiftLintCore/Models/Issue.swift +++ b/Source/SwiftLintCore/Models/Issue.swift @@ -84,7 +84,7 @@ public enum Issue: LocalizedError, Equatable { /// Flag to enable warnings for deprecations being printed to the console. Printing is enabled by default. package nonisolated(unsafe) static var printDeprecationWarnings = true - @TaskLocal private static var messageConsumer: (@Sendable (String) -> Void)? + @TaskLocal private static var printQueueContinuation: AsyncStream.Continuation? /// Hook used to capture all messages normally printed to stdout and return them back to the caller. /// @@ -95,18 +95,10 @@ public enum Issue: LocalizedError, Equatable { /// - returns: The collected messages produced while running the code in the runner. @MainActor static func captureConsole(runner: @Sendable () throws -> Void) async rethrows -> String { - actor Console { - static var content = "" - } - defer { - Console.content = "" - } - try $messageConsumer.withValue( - { Console.content += (Console.content.isEmpty ? "" : "\n") + $0 }, - operation: runner - ) - await Task.yield() - return Console.content + let (stream, continuation) = AsyncStream.makeStream(of: String.self) + try $printQueueContinuation.withValue(continuation, operation: runner) + continuation.finish() + return await stream.reduce(into: "") { @Sendable in $0 += $0.isEmpty ? $1 : "\n\($1)" } } /// Wraps any `Error` into a `SwiftLintError.genericWarning` if it is not already a `SwiftLintError`. @@ -140,13 +132,8 @@ public enum Issue: LocalizedError, Equatable { if case .ruleDeprecated = self, !Self.printDeprecationWarnings { return } - Task(priority: .high) { @MainActor in - if let consumer = Self.messageConsumer { - consumer(errorDescription) - } else { - queuedPrintError(errorDescription) - } - } + Self.printQueueContinuation?.yield(errorDescription) + queuedPrintError(errorDescription) } private var message: String { diff --git a/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift index 6108180ba8..3fe7ace943 100644 --- a/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/ExplicitTypeInterfaceConfigurationTests.swift @@ -25,11 +25,12 @@ final class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase { } func testInvalidKeyInCustomConfiguration() async throws { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var config = ExplicitTypeInterfaceConfiguration() - try config.apply(configuration: ["invalidKey": "error"]) - }, + let console = try await Issue.captureConsole { + var config = ExplicitTypeInterfaceConfiguration() + try config.apply(configuration: ["invalidKey": "error"]) + } + XCTAssertEqual( + console, "warning: Configuration for 'explicit_type_interface' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift index 498745be78..936dd77bfc 100644 --- a/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift +++ b/Tests/BuiltInRulesTests/IndentationWidthRuleTests.swift @@ -8,14 +8,15 @@ final class IndentationWidthRuleTests: SwiftLintTestCase { let defaultValue = IndentationWidthConfiguration().indentationWidth for indentation in [0, -1, -5] { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var testee = IndentationWidthConfiguration() - try testee.apply(configuration: ["indentation_width": indentation]) - - // Value remains the default. - XCTAssertEqual(testee.indentationWidth, defaultValue) - }, + let console = try await Issue.captureConsole { + var testee = IndentationWidthConfiguration() + try testee.apply(configuration: ["indentation_width": indentation]) + + // Value remains the default. + XCTAssertEqual(testee.indentationWidth, defaultValue) + } + XCTAssertEqual( + console, "warning: Invalid configuration for 'indentation_width' rule. Falling back to default." ) } diff --git a/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift index 6d19466b94..2915e344d4 100644 --- a/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/MultilineParametersConfigurationTests.swift @@ -6,13 +6,14 @@ import XCTest final class MultilineParametersConfigurationTests: SwiftLintTestCase { func testInvalidMaxNumberOfSingleLineParameters() async throws { for maxNumberOfSingleLineParameters in [0, -1] { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var config = MultilineParametersConfiguration() - try config.apply( - configuration: ["max_number_of_single_line_parameters": maxNumberOfSingleLineParameters] - ) - }, + let console = try await Issue.captureConsole { + var config = MultilineParametersConfiguration() + try config.apply( + configuration: ["max_number_of_single_line_parameters": maxNumberOfSingleLineParameters] + ) + } + XCTAssertEqual( + console, """ warning: Inconsistent configuration for 'multiline_parameters' rule: Option \ 'max_number_of_single_line_parameters' should be >= 1. @@ -22,13 +23,14 @@ final class MultilineParametersConfigurationTests: SwiftLintTestCase { } func testInvalidMaxNumberOfSingleLineParametersWithSingleLineEnabled() async throws { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var config = MultilineParametersConfiguration() - try config.apply( - configuration: ["max_number_of_single_line_parameters": 2, "allows_single_line": false] - ) - }, + let console = try await Issue.captureConsole { + var config = MultilineParametersConfiguration() + try config.apply( + configuration: ["max_number_of_single_line_parameters": 2, "allows_single_line": false] + ) + } + XCTAssertEqual( + console, """ warning: Inconsistent configuration for 'multiline_parameters' rule: Option \ 'max_number_of_single_line_parameters' has no effect when 'allows_single_line' is false. diff --git a/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift index f555bbbcb7..737172b732 100644 --- a/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift +++ b/Tests/BuiltInRulesTests/NoEmptyBlockConfigurationTests.swift @@ -23,11 +23,12 @@ final class NoEmptyBlockConfigurationTests: SwiftLintTestCase { } func testInvalidKeyInCustomConfiguration() async throws { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var config = NoEmptyBlockConfiguration() - try config.apply(configuration: ["invalidKey": "error"]) - }, + let console = try await Issue.captureConsole { + var config = NoEmptyBlockConfiguration() + try config.apply(configuration: ["invalidKey": "error"]) + } + XCTAssertEqual( + console, "warning: Configuration for 'no_empty_block' rule contains the invalid key(s) 'invalidKey'." ) } diff --git a/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift index ddda2f436d..842c2ee3f9 100644 --- a/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift +++ b/Tests/FrameworkTests/RuleConfigurationDescriptionTests.swift @@ -491,35 +491,36 @@ final class RuleConfigurationDescriptionTests: SwiftLintTestCase { } func testDeprecationWarning() async throws { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var configuration = TestConfiguration() - try configuration.apply(configuration: ["set": [6, 7]]) - }, + let console = try await Issue.captureConsole { + var configuration = TestConfiguration() + try configuration.apply(configuration: ["set": [6, 7]]) + } + XCTAssertEqual( + console, "warning: Configuration option 'set' in 'my_rule' rule is deprecated. Use the option 'other_opt' instead." ) } func testNoDeprecationWarningIfNoDeprecatedPropertySet() async throws { - try await AsyncAssertTrue( - try await Issue.captureConsole { - var configuration = TestConfiguration() - try configuration.apply(configuration: ["flag": false]) - }.isEmpty - ) + let console = try await Issue.captureConsole { + var configuration = TestConfiguration() + try configuration.apply(configuration: ["flag": false]) + } + XCTAssertTrue(console.isEmpty) } func testInvalidKeys() async throws { - try await AsyncAssertEqual( - try await Issue.captureConsole { - var configuration = TestConfiguration() - try configuration.apply(configuration: [ - "severity": "error", - "warning": 3, - "unknown": 1, - "unsupported": true, - ]) - }, + let console = try await Issue.captureConsole { + var configuration = TestConfiguration() + try configuration.apply(configuration: [ + "severity": "error", + "warning": 3, + "unknown": 1, + "unsupported": true, + ]) + } + XCTAssertEqual( + console, "warning: Configuration for 'RuleMock' rule contains the invalid key(s) 'unknown', 'unsupported'." ) } From a2032f9a49317ea2670c7dc66890edc7e044d494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Mon, 10 Feb 2025 23:03:14 +0100 Subject: [PATCH 255/260] Delete branch after merge --- .github/workflows/post-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index b0698f81bb..54b3656460 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -17,6 +17,7 @@ jobs: git fetch origin release/${{ github.event.release.tag_name }} git merge --ff-only release/${{ github.event.release.tag_name }} git push origin main + git push origin --delete release/${{ github.event.release.tag_name }} publish-pod: name: Publish Pod needs: merge-into-main From 2cdb8685a73a908e8e30ea006647081ab2c5bb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 11 Feb 2025 09:22:15 +0100 Subject: [PATCH 256/260] Restrict workflow permissions (#5996) --- .github/workflows/docker.yml | 4 ++++ .github/workflows/lint.yml | 4 ++++ .github/workflows/plugins-sync.yml | 3 +++ 3 files changed, 11 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 66762e21a1..19cdec68eb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -17,6 +17,10 @@ on: required: true type: string +permissions: + contents: read + packages: write + jobs: build: name: Build Docker Image diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 159a54e6ff..ad381550fc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,6 +5,10 @@ on: branches: - '*' +permissions: + contents: read + pull-requests: write + jobs: lint-swift: name: Lint Swift diff --git a/.github/workflows/plugins-sync.yml b/.github/workflows/plugins-sync.yml index bdd3d8fc5e..af38ed7d25 100644 --- a/.github/workflows/plugins-sync.yml +++ b/.github/workflows/plugins-sync.yml @@ -8,6 +8,9 @@ on: - 'Plugins/**' workflow_dispatch: +permissions: + contents: read + jobs: sync: name: Sync Plugins Folder From b1198b77d5495bdc952b96bcb78f6ff0a930d619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 11 Feb 2025 23:03:48 +0100 Subject: [PATCH 257/260] Restrict permissions of remaining workflows (#5997) --- .github/workflows/post-release.yml | 7 +++++++ .github/workflows/release.yml | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 54b3656460..6c383fcc87 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -8,6 +8,8 @@ jobs: merge-into-main: name: Merge into main runs-on: ubuntu-24.04 + permissions: + contents: write steps: - uses: actions/checkout@v4 with: @@ -22,6 +24,8 @@ jobs: name: Publish Pod needs: merge-into-main runs-on: macOS-14 + permissions: + contents: read steps: - uses: actions/checkout@v4 with: @@ -40,6 +44,8 @@ jobs: name: Dispatch Plugins Repository needs: merge-into-main runs-on: ubuntu-24.04 + permissions: + contents: read steps: - name: Checkout repository uses: actions/checkout@v4 @@ -66,6 +72,7 @@ jobs: runs-on: ubuntu-24.04 container: image: ghcr.io/homebrew/ubuntu24.04:latest + permissions: {} steps: - name: Retrieve author in uppercase id: retrieve_author diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41126e59c3..65a735084d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,8 @@ jobs: author_name: ${{ steps.configure_git_author.outputs.name }} author_uppercase: ${{ steps.retrieve_author.outputs.name }} author_email: ${{ steps.configure_git_author.outputs.email }} + permissions: + contents: write steps: - name: Checkout repository uses: actions/checkout@v4 @@ -72,6 +74,9 @@ jobs: name: Build Linux Binaries needs: prepare-release uses: ./.github/workflows/docker.yml + permissions: + contents: read + packages: write secrets: inherit with: ref: release/${{ inputs.version }} @@ -80,6 +85,8 @@ jobs: name: Build macOS Binaries needs: prepare-release runs-on: macOS-14 + permissions: + contents: read steps: - uses: actions/checkout@v4 with: @@ -103,6 +110,9 @@ jobs: - build-docker - build-macos runs-on: macOS-14 + permissions: + actions: read + contents: write steps: - uses: actions/checkout@v4 with: From 364442ae6c18b98749ef68dcf96da382b1514a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 12 Feb 2025 20:27:10 +0100 Subject: [PATCH 258/260] Move `--only-rule` option to common arguments (#5981) --- Source/swiftlint/Commands/Analyze.swift | 10 +--------- Source/swiftlint/Commands/Lint.swift | 10 +--------- Source/swiftlint/Common/LintOrAnalyzeArguments.swift | 8 ++++++++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Source/swiftlint/Commands/Analyze.swift b/Source/swiftlint/Commands/Analyze.swift index 10471b1dda..5ae0f3ff3c 100644 --- a/Source/swiftlint/Commands/Analyze.swift +++ b/Source/swiftlint/Commands/Analyze.swift @@ -13,14 +13,6 @@ extension SwiftLint { var compilerLogPath: String? @Option(help: "The path of a compilation database to use when running AnalyzerRules.") var compileCommands: String? - @Option( - parsing: .singleValue, - help: """ - Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`. - Can be specified repeatedly to run multiple rules. - """ - ) - var onlyRule: [String] = [] @Argument(help: pathsArgumentDescription(for: .analyze)) var paths = [String]() @@ -49,7 +41,7 @@ extension SwiftLint { cachePath: nil, ignoreCache: true, enableAllRules: false, - onlyRule: onlyRule, + onlyRule: common.onlyRule, autocorrect: common.fix, format: common.format, compilerLogPath: compilerLogPath, diff --git a/Source/swiftlint/Commands/Lint.swift b/Source/swiftlint/Commands/Lint.swift index c1edb053b7..eb267c7148 100644 --- a/Source/swiftlint/Commands/Lint.swift +++ b/Source/swiftlint/Commands/Lint.swift @@ -19,14 +19,6 @@ extension SwiftLint { var noCache = false @Flag(help: "Run all rules, even opt-in and disabled ones, ignoring `only_rules`.") var enableAllRules = false - @Option( - parsing: .singleValue, - help: """ - Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`. - Can be specified repeatedly to run multiple rules. - """ - ) - var onlyRule: [String] = [] @Argument(help: pathsArgumentDescription(for: .lint)) var paths = [String]() @@ -61,7 +53,7 @@ extension SwiftLint { cachePath: cachePath, ignoreCache: noCache, enableAllRules: enableAllRules, - onlyRule: onlyRule, + onlyRule: common.onlyRule, autocorrect: common.fix, format: common.format, compilerLogPath: nil, diff --git a/Source/swiftlint/Common/LintOrAnalyzeArguments.swift b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift index 2e545aa7ae..e956dd13ba 100644 --- a/Source/swiftlint/Common/LintOrAnalyzeArguments.swift +++ b/Source/swiftlint/Common/LintOrAnalyzeArguments.swift @@ -53,6 +53,14 @@ struct LintOrAnalyzeArguments: ParsableArguments { var progress = false @Flag(help: "Check whether a later version of SwiftLint is available after processing all files.") var checkForUpdates = false + @Option( + parsing: .singleValue, + help: """ + Run only the specified rule, ignoring `only_rules`, `opt_in_rules` and `disabled_rules`. + Can be specified repeatedly to run multiple rules. + """ + ) + var onlyRule: [String] = [] } // MARK: - Common Argument Help From 6c8e8993ee4945ee8bb351abbb8d4840ea09d69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 12 Feb 2025 20:27:33 +0100 Subject: [PATCH 259/260] Exclude `@Suite` types and `@Test` functions from `no_magic_numbers` analysis (#5968) --- CHANGELOG.md | 5 +- .../Rules/Idiomatic/NoMagicNumbersRule.swift | 60 +++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f18951406..5386018ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,10 @@ ### Enhancements -* None. +* Exclude types with a `@Suite` attribute and functions annotated with `@Test` from `no_magic_numbers` rule. + Also treat a type as a `@Suite` if it contains `@Test` functions. + [SimplyDanny](https://github.com/SimplyDanny) + [#5964](https://github.com/realm/SwiftLint/issues/5964) ### Bug Fixes diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift index 7cf3157800..8c5e6da19b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoMagicNumbersRule.swift @@ -81,9 +81,26 @@ struct NoMagicNumbersRule: Rule { Example("let (lowerBound, upperBound) = (400, 599)"), Example("let a = (5, 10)"), Example("let notFound = (statusCode: 404, description: \"Not Found\", isError: true)"), + Example("#Preview { ContentView(value: 5) }"), + Example("@Test func f() { let _ = 2 + 2 }"), Example(""" - #Preview { - ContentView(value: 5) + @Suite struct Test { + @Test func f() { + func g() { let _ = 2 + 2 } + let _ = 2 + 2 + } + } + """), + Example(""" + @Suite actor Test { + private var a: Int { 2 } + @Test func f() { let _ = 2 + a } + } + """), + Example(""" + class Test { // @Suite implicitly + private var a: Int { 2 } + @Test func f() { let _ = 2 + a } } """), ], @@ -127,12 +144,12 @@ private extension NoMagicNumbersRule { private var nonTestClasses: Set = [] private var possibleViolations: [String: Set] = [:] - override func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind { - node.isSimpleTupleAssignment ? .skipChildren : .visitChildren + override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { + node.isTestSuite ? .skipChildren : .visitChildren } - override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind { - node.macroName.text == "Preview" ? .skipChildren : .visitChildren + override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { + node.isTestSuite ? .skipChildren : .visitChildren } override func visitPost(_ node: ClassDeclSyntax) { @@ -145,6 +162,10 @@ private extension NoMagicNumbersRule { } } + override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { + node.isTestSuite ? .skipChildren : .visitChildren + } + override func visitPost(_ node: FloatLiteralExprSyntax) { guard node.literal.isMagicNumber else { return @@ -152,6 +173,10 @@ private extension NoMagicNumbersRule { collectViolation(forNode: node) } + override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { + node.attributes.contains(attributeNamed: "Test") ? .skipChildren : .visitChildren + } + override func visitPost(_ node: IntegerLiteralExprSyntax) { guard node.literal.isMagicNumber else { return @@ -159,6 +184,18 @@ private extension NoMagicNumbersRule { collectViolation(forNode: node) } + override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind { + node.macroName.text == "Preview" ? .skipChildren : .visitChildren + } + + override func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind { + node.isSimpleTupleAssignment ? .skipChildren : .visitChildren + } + + override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { + node.isTestSuite ? .skipChildren : .visitChildren + } + private func collectViolation(forNode node: some ExprSyntaxProtocol) { if node.isMemberOfATestClass(configuration.testParentClasses) { return @@ -190,6 +227,17 @@ private extension NoMagicNumbersRule { } } +private extension DeclGroupSyntax { + var isTestSuite: Bool { + if attributes.contains(attributeNamed: "Suite") { + return true + } + return memberBlock.members.contains { + $0.decl.as(FunctionDeclSyntax.self)?.attributes.contains(attributeNamed: "Test") == true + } + } +} + private extension TokenSyntax { var isMagicNumber: Bool { guard let number = Double(text.replacingOccurrences(of: "_", with: "")) else { From 82736d1925de388e6b5abb4c5a293393043608fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Wed, 12 Feb 2025 22:46:28 +0100 Subject: [PATCH 260/260] Add new `opaque_over_existential` rule (#5915) --- CHANGELOG.md | 4 + .../Models/BuiltInRules.swift | 1 + .../OpaqueOverExistentialParameterRule.swift | 93 +++++++++++++++++++ .../Protocols/CollectingRule.swift | 9 +- Source/SwiftLintCore/Protocols/Rule.swift | 4 +- Source/swiftlint/Commands/Rules.swift | 19 ++-- Tests/GeneratedTests/GeneratedTests.swift | 6 ++ .../default_rule_configurations.yml | 4 + 8 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/OpaqueOverExistentialParameterRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 5386018ae8..46a776effd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ [SimplyDanny](https://github.com/SimplyDanny) [#5964](https://github.com/realm/SwiftLint/issues/5964) +* Add new `opaque_over_existential` opt-in rule that triggers when the existential `any` type of a + function parameter can be replaced with an opaque `some` type. + [SimplyDanny](https://github.com/SimplyDanny) + ### Bug Fixes * Fix issue referencing the Tests package from another Bazel workspace. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index c0403acbaa..6d25c0e66a 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -139,6 +139,7 @@ public let builtInRules: [any Rule.Type] = [ NumberSeparatorRule.self, ObjectLiteralRule.self, OneDeclarationPerFileRule.self, + OpaqueOverExistentialParameterRule.self, OpeningBraceRule.self, OperatorFunctionWhitespaceRule.self, OperatorUsageWhitespaceRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/OpaqueOverExistentialParameterRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/OpaqueOverExistentialParameterRule.swift new file mode 100644 index 0000000000..c5547bb18d --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/OpaqueOverExistentialParameterRule.swift @@ -0,0 +1,93 @@ +import SwiftSyntax + +@SwiftSyntaxRule(correctable: true, optIn: true) +struct OpaqueOverExistentialParameterRule: Rule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "opaque_over_existential", + name: "Opaque Over Existential Parameter", + description: "Prefer opaque over existential type in function parameter", + kind: .lint, + nonTriggeringExamples: [ + Example("func f(_: some P) {}"), + Example("func f(_: (some P)?) {}"), + Example("func f(_: some P & Q) {}"), + Example("func f(_: any P.Type) {}"), + Example("func f(_: (any P.Type)?) {}"), + Example("func f(_: borrowing any ~Copyable.Type) {}"), + Example("func f(_: () -> Int = { let i: any P = p(); return i.get() }) {}"), + Example("func f(_: [any P]) {}"), + Example("func f(_: [any P: any Q]) {}"), + Example("func f(_: () -> any P) {}"), + ], + triggeringExamples: [ + Example("func f(_: ↓any P) {}"), + Example("func f(_: (↓any P)?) {}"), + Example("func f(_: ↓any P & Q) {}"), + Example("func f(_: borrowing ↓any ~Copyable) {}"), + Example("func f(_: borrowing (↓any ~Copyable)?) {}"), + Example("func f(_: (↓any P, ↓any Q)) {}"), + ], + corrections: [ + Example("func f(_: any P) {}"): + Example("func f(_: some P) {}"), + Example("func f(_: (any P)?) {}"): + Example("func f(_: (some P)?) {}"), + Example("func f(_: any P & Q) {}"): + Example("func f(_: some P & Q) {}"), + Example("func f(_: /* comment */ any P/* comment*/) {}"): + Example("func f(_: /* comment */ some P/* comment*/) {}"), + Example("func f(_: borrowing (any ~Copyable)?) {}"): + Example("func f(_: borrowing (some ~Copyable)?) {}"), + ] + ) +} + +private extension OpaqueOverExistentialParameterRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: FunctionParameterSyntax) { + AnyTypeVisitor(viewMode: .sourceAccurate).walk(tree: node.type, handler: \.anyRanges).forEach { range in + violations.append(.init( + position: range.start, + correction: .init(start: range.start, end: range.end, replacement: "some") + )) + } + } + } +} + +private class AnyTypeVisitor: SyntaxVisitor { + var anyRanges = [(start: AbsolutePosition, end: AbsolutePosition)]() + + override func visitPost(_ node: SomeOrAnyTypeSyntax) { + let specifier = node.someOrAnySpecifier + if specifier.tokenKind == .keyword(.any), !node.constraint.isMetaType { + anyRanges.append((specifier.positionAfterSkippingLeadingTrivia, specifier.endPositionBeforeTrailingTrivia)) + } + } + + override func visit(_: ArrayTypeSyntax) -> SyntaxVisitorContinueKind { + .skipChildren + } + + override func visit(_: DictionaryTypeSyntax) -> SyntaxVisitorContinueKind { + .skipChildren + } + + override func visit(_: FunctionTypeSyntax) -> SyntaxVisitorContinueKind { + .skipChildren + } +} + +private extension TypeSyntax { + var isMetaType: Bool { + if `is`(MetatypeTypeSyntax.self) { + return true + } + if let suppressedType = `as`(SuppressedTypeSyntax.self) { + return suppressedType.type.isMetaType + } + return false + } +} diff --git a/Source/SwiftLintCore/Protocols/CollectingRule.swift b/Source/SwiftLintCore/Protocols/CollectingRule.swift index eb7bff0541..67ad891a0d 100644 --- a/Source/SwiftLintCore/Protocols/CollectingRule.swift +++ b/Source/SwiftLintCore/Protocols/CollectingRule.swift @@ -94,7 +94,12 @@ public extension CollectingRule where Self: AnalyzerRule { /// :nodoc: public extension Array where Element == any Rule { static func == (lhs: Array, rhs: Array) -> Bool { - if lhs.count != rhs.count { return false } - return !zip(lhs, rhs).contains { !$0.0.isEqualTo($0.1) } + guard lhs.count == rhs.count else { + return false + } + return !zip(lhs, rhs).contains { pair in + let first = pair.0, second = pair.1 + return !first.isEqualTo(second) + } } } diff --git a/Source/SwiftLintCore/Protocols/Rule.swift b/Source/SwiftLintCore/Protocols/Rule.swift index 5e0214b678..1d3500f615 100644 --- a/Source/SwiftLintCore/Protocols/Rule.swift +++ b/Source/SwiftLintCore/Protocols/Rule.swift @@ -48,7 +48,7 @@ public protocol Rule { /// - parameter rule: The `rule` value to compare against. /// /// - returns: Whether or not the specified rule is equivalent to the current rule. - func isEqualTo(_ rule: any Rule) -> Bool + func isEqualTo(_ rule: some Rule) -> Bool /// Collects information for the specified file in a storage object, to be analyzed by a `CollectedLinter`. /// @@ -110,7 +110,7 @@ public extension Rule { validate(file: file) } - func isEqualTo(_ rule: any Rule) -> Bool { + func isEqualTo(_ rule: some Rule) -> Bool { if let rule = rule as? Self { return configuration == rule.configuration } diff --git a/Source/swiftlint/Commands/Rules.swift b/Source/swiftlint/Commands/Rules.swift index ea79fc957a..930b41e317 100644 --- a/Source/swiftlint/Commands/Rules.swift +++ b/Source/swiftlint/Commands/Rules.swift @@ -36,10 +36,7 @@ extension SwiftLint { .list .sorted { $0.0 < $1.0 } if configOnly { - rules - .map(\.value) - .map { createInstance(of: $0, using: configuration, configure: !defaultConfig) } - .forEach { printConfig(for: $0) } + rules.forEach { printConfig(for: createInstance(of: $0.value, using: configuration)) } } else { let table = TextTable( ruleList: rules, @@ -54,7 +51,7 @@ extension SwiftLint { private func printDescription(for ruleType: any Rule.Type, with configuration: Configuration) { let description = ruleType.description - let rule = createInstance(of: ruleType, using: configuration, configure: !defaultConfig) + let rule = createInstance(of: ruleType, using: configuration) if configOnly { printConfig(for: rule) return @@ -76,7 +73,7 @@ extension SwiftLint { } } - private func printConfig(for rule: any Rule) { + private func printConfig(for rule: some Rule) { let configDescription = rule.createConfigurationDescription() if configDescription.hasContent { print("\(type(of: rule).identifier):") @@ -84,12 +81,10 @@ extension SwiftLint { } } - private func createInstance(of ruleType: any Rule.Type, - using config: Configuration, - configure: Bool) -> any Rule { - configure - ? config.configuredRule(forID: ruleType.identifier) ?? ruleType.init() - : ruleType.init() + private func createInstance(of ruleType: any Rule.Type, using config: Configuration) -> any Rule { + defaultConfig + ? ruleType.init() + : config.configuredRule(forID: ruleType.identifier) ?? ruleType.init() } } } diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index e5b5625317..31c22d3903 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -823,6 +823,12 @@ final class OneDeclarationPerFileRuleGeneratedTests: SwiftLintTestCase { } } +final class OpaqueOverExistentialParameterRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(OpaqueOverExistentialParameterRule.description) + } +} + final class OpeningBraceRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(OpeningBraceRule.description) diff --git a/Tests/IntegrationTests/default_rule_configurations.yml b/Tests/IntegrationTests/default_rule_configurations.yml index 5d667a9847..44a66483b2 100644 --- a/Tests/IntegrationTests/default_rule_configurations.yml +++ b/Tests/IntegrationTests/default_rule_configurations.yml @@ -649,6 +649,10 @@ one_declaration_per_file: severity: warning meta: opt-in: true +opaque_over_existential: + severity: warning + meta: + opt-in: true opening_brace: severity: warning ignore_multiline_type_headers: false