From 8b2776fe3decb6fd801a0e10924eec990e9b4b57 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 31 May 2024 08:20:00 -0700 Subject: [PATCH 01/11] Remove "Platforms: macOS" supported platforms banner (#271) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc97429..392b406 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Airbnb Swift Style Guide -[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fairbnb%2Fswift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/airbnb/swift) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fairbnb%2Fswift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/airbnb/swift) +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fairbnb%2Fswift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/airbnb/swift) ## Goals From 2c1c76a34cf4221becb8f2de0aee8cfb9ab040f2 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 7 Jun 2024 16:51:43 -0700 Subject: [PATCH 02/11] Add rule to infer property types from the right-hand-side value rather than writing the type explicitly on the left-hand side (#263) --- Package.swift | 4 +- README.md | 120 ++++++++++++++++-- .../AirbnbSwiftFormatTool/airbnb.swiftformat | 17 +-- 3 files changed, 121 insertions(+), 20 deletions(-) diff --git a/Package.swift b/Package.swift index a733512..579c10c 100644 --- a/Package.swift +++ b/Package.swift @@ -42,8 +42,8 @@ let package = Package( .binaryTarget( name: "SwiftFormat", - url: "https://github.com/calda/SwiftFormat/releases/download/0.54-beta-5/SwiftFormat.artifactbundle.zip", - checksum: "7447986db45a51164d23672c07f971406a4c0589b0c423fcb85e95ed8f8e7e48"), + url: "https://github.com/calda/SwiftFormat/releases/download/0.54-beta-7/SwiftFormat.artifactbundle.zip", + checksum: "0cf117050e7838f545009bfe4a75dbda98cff737cb847a7d065a89683e9e890a"), .binaryTarget( name: "SwiftLintBinary", diff --git a/README.md b/README.md index 392b406..92b791e 100644 --- a/README.md +++ b/README.md @@ -333,25 +333,125 @@ _You can enable the following settings in Xcode by running [this script](resourc ```swift // WRONG - let host: Host = Host() + let sun: Star = Star(mass: 1.989e30) + let earth: Planet = Planet.earth // RIGHT - let host = Host() + let sun = Star(mass: 1.989e30) + let earth = Planet.earth + + // NOT RECOMMENDED. However, since the linter doesn't have full type information, this is not enforced automatically. + let moon: Moon = earth.moon // returns `Moon` + + // RIGHT + let moon = earth.moon + let moon: PlanetaryBody? = earth.moon + + // WRONG: Most literals provide a default type that can be inferred. + let enableGravity: Bool = true + let numberOfPlanets: Int = 8 + let sunMass: Double = 1.989e30 + + // RIGHT + let enableGravity = true + let numberOfPlanets = 8 + let sunMass = 1.989e30 + + // WRONG: Types can be inferred from if/switch expressions as well if each branch has the same explicit type. + let smallestPlanet: Planet = + if treatPlutoAsPlanet { + Planet.pluto + } else { + Planet.mercury + } + + // RIGHT + let smallestPlanet = + if treatPlutoAsPlanet { + Planet.pluto + } else { + Planet.mercury + } ``` + + +* (link) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** [![SwiftFormat: preferInferredTypes](https://img.shields.io/badge/SwiftFormat-preferInferredTypes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#preferInferredTypes) + +
+ + Prefer using inferred types when the right-hand-side value is a static member with a leading dot (e.g. an `init`, a `static` property / function, or an enum case). This applies to both local variables and property declarations: + ```swift - enum Direction { - case left - case right + // WRONG + struct SolarSystemBuilder { + let sun: Star = .init(mass: 1.989e30) + let earth: Planet = .earth + + func setUp() { + let galaxy: Galaxy = .andromeda + let system: SolarSystem = .init(sun, earth) + galaxy.add(system) + } } + + // RIGHT + struct SolarSystemBuilder { + let sun = Star(mass: 1.989e30) + let earth = Planet.earth - func someDirection() -> Direction { - // WRONG - return Direction.left + func setUp() { + let galaxy = Galaxy.andromeda + let system = SolarSystem(sun, earth) + galaxy.add(system) + } + } + ``` - // RIGHT - return .left + Explicit types are still permitted in other cases: + + ```swift + // RIGHT: There is no right-hand-side value, so an explicit type is required. + let sun: Star + + // RIGHT: The right-hand-side is not a static member of the left-hand type. + let moon: PlantaryBody = earth.moon + let sunMass: Float = 1.989e30 + let planets: [Planet] = [] + let venusMoon: Moon? = nil + ``` + + There are some rare cases where the inferred type syntax has a different meaning than the explicit type syntax. In these cases, the explicit type syntax is still permitted: + + ```swift + extension String { + static let earth = "Earth" } + + // WRONG: fails with "error: type 'String?' has no member 'earth'" + let planetName = String?.earth + + // RIGHT + let planetName: String? = .earth + ``` + + ```swift + struct SaturnOutline: ShapeStyle { ... } + + extension ShapeStyle where Self == SaturnOutline { + static var saturnOutline: SaturnOutline { + SaturnOutline() + } + } + + // WRONG: fails with "error: static member 'saturnOutline' cannot be used on protocol metatype '(any ShapeStyle).Type'" + let myShape2 = (any ShapeStyle).myShape + + // RIGHT: If the property's type is an existential / protocol type, moving the type + // to the right-hand side will result in invalid code if the value is defined in an + // extension like `extension ShapeStyle where Self == SaturnOutline`. + // SwiftFormat autocorrect detects this case by checking for the existential `any` keyword. + let myShape1: any ShapeStyle = .saturnOutline ```
diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index ce2fa62..bcb274a 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -29,16 +29,16 @@ --organizetypes class,struct,enum,extension,actor # organizeDeclarations --extensionacl on-declarations # extensionAccessControl --patternlet inline # hoistPatternLet ---redundanttype inferred # redundantType +--redundanttype inferred # redundantType, propertyType --typeblanklines preserve # blankLinesAtStartOfScope, blankLinesAtEndOfScope --emptybraces spaced # emptyBraces --someAny disabled # opaqueGenericParameters ---elseposition same-line #elseOnSameLine ---guardelse next-line #elseOnSameLine ---onelineforeach convert #preferForLoop ---shortoptionals always #typeSugar ---semicolons never #semicolons ---doccomments preserve #docComments +--elseposition same-line # elseOnSameLine +--guardelse next-line # elseOnSameLine +--onelineforeach convert # preferForLoop +--shortoptionals always # typeSugar +--semicolons never # semicolons +--doccomments preserve # docComments # We recommend a max width of 100 but _strictly enforce_ a max width of 130 --maxwidth 130 # wrap @@ -102,4 +102,5 @@ --rules wrapMultilineConditionalAssignment --rules blankLineAfterMultilineSwitchCase --rules consistentSwitchStatementSpacing ---rules semicolons \ No newline at end of file +--rules semicolons +--rules propertyType From cdf293c0c2f84420efccd401b7ef491cfc379e08 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 7 Jun 2024 18:04:07 -0700 Subject: [PATCH 03/11] Fix name of propertyType rule --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92b791e..2f14f80 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,7 @@ _You can enable the following settings in Xcode by running [this script](resourc -* (link) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** [![SwiftFormat: preferInferredTypes](https://img.shields.io/badge/SwiftFormat-preferInferredTypes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#preferInferredTypes) +* (link) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** [![SwiftFormat: propertyType](https://img.shields.io/badge/SwiftFormat-propertyType-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#propertyType)
From 7cf38c51489027823d65926ccf8bc6ce485c8f0b Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Tue, 11 Jun 2024 11:02:17 -0700 Subject: [PATCH 04/11] Add command to update SwiftFormat reference to latest version (#274) --- .github/actions/setup/action.yml | 3 ++ .github/workflows/main.yml | 13 ++++++-- Gemfile | 3 ++ Gemfile.lock | 17 ++++++++++ Rakefile | 54 ++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 9bacea6..60a29c0 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -10,3 +10,6 @@ runs: run: sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode }}.app if: ${{ inputs.xcode }} shell: bash + - name: Install Ruby Gems + run: bundle install + shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5f22654..338ece1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,8 +17,11 @@ jobs: - '13.4.1' # Swift 5.6 steps: - uses: actions/checkout@v2 + - uses: ./.github/actions/setup + with: + xcode: ${{ matrix.xcode }} - name: Test Package Plugin - run: swift package --allow-writing-to-package-directory format --lint + run: bundle exec rake lint:swift test-package-plugin-macos-13: name: Test Package Plugin @@ -32,8 +35,11 @@ jobs: - '15.0' # Swift 5.9 steps: - uses: actions/checkout@v2 + - uses: ./.github/actions/setup + with: + xcode: ${{ matrix.xcode }} - name: Test Package Plugin - run: swift package --allow-writing-to-package-directory format --lint + run: bundle exec rake lint:swift unit-tests: name: Unit Tests @@ -45,5 +51,8 @@ jobs: - '15.0' # Swift 5.9 steps: - uses: actions/checkout@v2 + - uses: ./.github/actions/setup + with: + xcode: ${{ matrix.xcode }} - name: Run Unit Tests run: swift test diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..9c085a2 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' do + gem "rake", "~> 13.0.0" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..2ffc012 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,17 @@ +GEM + specs: + +GEM + remote: https://rubygems.org/ + specs: + rake (13.0.6) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + rake (~> 13.0.0)! + +BUNDLED WITH + 2.5.4 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..72c096f --- /dev/null +++ b/Rakefile @@ -0,0 +1,54 @@ +require 'json' +require 'net/http' +require 'json' +require 'tempfile' + +namespace :lint do + desc 'Lints swift files' + task :swift do + sh 'swift package --allow-writing-to-package-directory format --lint' + end +end + +namespace :format do + desc 'Formats swift files' + task :swift do + sh 'swift package --allow-writing-to-package-directory format' + end +end + +namespace :update do + desc 'Updates SwiftFormat to the latest version' + task :swiftformat do + # Find the most recent release of SwiftFormat in the https://github.com/calda/SwiftFormat repo. + response = Net::HTTP.get(URI('https://api.github.com/repos/calda/SwiftFormat/releases/latest')) + latest_release_info = JSON.parse(response) + + latest_version_number = latest_release_info['tag_name'] + + # Download the artifact bundle for the latest release and compute its checksum. + temp_dir = Dir.mktmpdir + artifact_bundle_url = "https://github.com/calda/SwiftFormat/releases/download/#{latest_version_number}/swiftformat.artifactbundle.zip" + artifact_bundle_zip_path = "#{temp_dir}/swiftformat.artifactbundle.zip" + + sh "curl #{artifact_bundle_url} -L --output #{artifact_bundle_zip_path}" + checksum = `swift package compute-checksum #{artifact_bundle_zip_path}` + + # Update the Package.swift file to reference this version + package_manifest_path = 'Package.swift' + package_manifest_content = File.read(package_manifest_path) + + updated_swift_format_reference = <<-EOS + .binaryTarget( + name: "SwiftFormat", + url: "https://github.com/calda/SwiftFormat/releases/download/#{latest_version_number}/SwiftFormat.artifactbundle.zip", + checksum: "#{checksum.strip}"), + EOS + + regex = /[ ]*.binaryTarget\([\S\s]*name: "SwiftFormat"[\S\s]*?\),\s/ + updated_package_manifest = package_manifest_content.gsub(regex, updated_swift_format_reference) + File.open(package_manifest_path, "w") { |file| file.puts updated_package_manifest } + + puts "Updated Package.swift to reference SwiftFormat #{latest_version_number}" + end +end From 77bb672e9dc42110e00ead06fb16fa27f2ab94bd Mon Sep 17 00:00:00 2001 From: Manny Lopez Date: Wed, 12 Jun 2024 11:21:30 -0700 Subject: [PATCH 05/11] Update SwiftFormat to v. 0.55-beta-2 (#275) Co-authored-by: Cal Stephens --- Package.swift | 8 ++++---- Rakefile | 4 ++-- Sources/AirbnbSwiftFormatTool/airbnb.swiftformat | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Package.swift b/Package.swift index 579c10c..f086696 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( ]), dependencies: [ "AirbnbSwiftFormatTool", - "SwiftFormat", + "swiftformat", "SwiftLintBinary", ]), @@ -41,9 +41,9 @@ let package = Package( dependencies: ["AirbnbSwiftFormatTool"]), .binaryTarget( - name: "SwiftFormat", - url: "https://github.com/calda/SwiftFormat/releases/download/0.54-beta-7/SwiftFormat.artifactbundle.zip", - checksum: "0cf117050e7838f545009bfe4a75dbda98cff737cb847a7d065a89683e9e890a"), + name: "swiftformat", + url: "https://github.com/calda/SwiftFormat/releases/download/0.55-beta-2/SwiftFormat.artifactbundle.zip", + checksum: "f7ba281b879af7920e368144117269ba00abcc589b6d36f47ea0c21e62410a7c"), .binaryTarget( name: "SwiftLintBinary", diff --git a/Rakefile b/Rakefile index 72c096f..b5a05bf 100644 --- a/Rakefile +++ b/Rakefile @@ -40,12 +40,12 @@ namespace :update do updated_swift_format_reference = <<-EOS .binaryTarget( - name: "SwiftFormat", + name: "swiftformat", url: "https://github.com/calda/SwiftFormat/releases/download/#{latest_version_number}/SwiftFormat.artifactbundle.zip", checksum: "#{checksum.strip}"), EOS - regex = /[ ]*.binaryTarget\([\S\s]*name: "SwiftFormat"[\S\s]*?\),\s/ + regex = /[ ]*.binaryTarget\([\S\s]*name: "swiftformat"[\S\s]*?\),\s/ updated_package_manifest = package_manifest_content.gsub(regex, updated_swift_format_reference) File.open(package_manifest_path, "w") { |file| file.puts updated_package_manifest } diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index bcb274a..bdd47a6 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -100,7 +100,7 @@ --rules preferForLoop --rules conditionalAssignment --rules wrapMultilineConditionalAssignment ---rules blankLineAfterMultilineSwitchCase ---rules consistentSwitchStatementSpacing +--rules blankLineAfterSwitchCase +--rules consistentSwitchCaseSpacing --rules semicolons --rules propertyType From 61e3b65ccf2bcd8de4f5db45680fb37b1893d421 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 12 Jun 2024 14:23:52 -0700 Subject: [PATCH 06/11] Fix sample code in omit-return rule --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2f14f80..5704a85 100644 --- a/README.md +++ b/README.md @@ -3128,6 +3128,7 @@ _You can enable the following settings in Xcode by running [this script](resourc return "💥 Critical Error" } else { return "ℹ️ Info" + } } func type(of planet: Planet) -> PlanetType { @@ -3160,6 +3161,7 @@ _You can enable the following settings in Xcode by running [this script](resourc "💥 Critical Error" } else { "ℹ️ Info" + } } func type(of planet: Planet) -> PlanetType { From 51f447caa67862c34dad1a26f4e71c9756ef9e8d Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 12 Jun 2024 14:24:57 -0700 Subject: [PATCH 07/11] Update rule names for `blankLineAfterSwitchCase` and `consistentSwitchCaseSpacing` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5704a85..ab14b7d 100644 --- a/README.md +++ b/README.md @@ -1340,7 +1340,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Insert a blank line following a switch case with a multi-line body.** Spacing within an individual switch statement should be consistent. If any case has a multi-line body then all cases should include a trailing blank line. The last switch case doesn't need a blank line, since it is already followed by a closing brace. [![SwiftFormat: blankLineAfterMultilineSwitchCase](https://img.shields.io/badge/SwiftFormat-blankLineAfterMultilineSwitchCase-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#blankLineAfterMultilineSwitchCase) [![SwiftFormat: consistentSwitchStatementSpacing](https://img.shields.io/badge/SwiftFormat-consistentSwitchStatementSpacing-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#consistentSwitchStatementSpacing) +* (link) **Insert a blank line following a switch case with a multi-line body.** Spacing within an individual switch statement should be consistent. If any case has a multi-line body then all cases should include a trailing blank line. The last switch case doesn't need a blank line, since it is already followed by a closing brace. [![SwiftFormat: blankLineAfterSwitchCase](https://img.shields.io/badge/SwiftFormat-blankLineAfterSwitchCase-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#blankLineAfterSwitchCase) [![SwiftFormat: consistentSwitchCaseSpacing](https://img.shields.io/badge/SwiftFormat-consistentSwitchCaseSpacing-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#consistentSwitchCaseSpacing)
From f37baa74b8a8d96fc953309b8fe09e837b2debd3 Mon Sep 17 00:00:00 2001 From: Manny Lopez Date: Fri, 14 Jun 2024 11:17:02 -0700 Subject: [PATCH 08/11] Add rule to remove blank lines between chained functions (#272) --- .gitignore | 3 +- README.md | 44 +++++++++++++++++++ .../AirbnbSwiftFormatTool/airbnb.swiftformat | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 536ad55..b69502f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .build .swiftpm -.DS_Store \ No newline at end of file +.DS_Store +.vscode \ No newline at end of file diff --git a/README.md b/README.md index ab14b7d..b635745 100644 --- a/README.md +++ b/README.md @@ -2191,6 +2191,50 @@ _You can enable the following settings in Xcode by running [this script](resourc
+* (link) **Remove blank lines between chained functions.** [![SwiftFormat: blanklinesbetweenchainedfunctions](https://img.shields.io/badge/SwiftFormat-blankLinesBetweenChainedFunctions-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#blanklinesbetweenchainedfunctions) + +
+ + #### Why? + + Improves readability and maintainability, making it easier to see the sequence of functions that are applied to the object. + + ```swift + // WRONG + var innerPlanetNames: [String] { + planets + .filter { $0.isInnerPlanet } + + .map { $0.name } + } + + // WRONG + var innerPlanetNames: [String] { + planets + .filter { $0.isInnerPlanet } + + // Gets the name of the inner planet + .map { $0.name } + } + + // RIGHT + var innerPlanetNames: [String] { + planets + .filter { $0.isInnerPlanet } + .map { $0.name } + } + + // RIGHT + var innerPlanetNames: [String] { + planets + .filter { $0.isInnerPlanet } + // Gets the name of the inner planet + .map { $0.name } + } + ``` + +
+ ### Closures * (link) **Favor `Void` return types over `()` in closure declarations.** If you must specify a `Void` return type in a function declaration, use `Void` rather than `()` to improve readability. [![SwiftLint: void_return](https://img.shields.io/badge/SwiftLint-void__return-007A87.svg)](https://realm.github.io/SwiftLint/void_return) diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index bdd47a6..632cb02 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -104,3 +104,4 @@ --rules consistentSwitchCaseSpacing --rules semicolons --rules propertyType +--rules blankLinesBetweenChainedFunctions \ No newline at end of file From 7a899538927d6a132772514ff67283210e7ab598 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 14 Jun 2024 15:35:21 -0700 Subject: [PATCH 09/11] Add redundantProperty rule (#273) --- README.md | 36 +++++++++++++++++++ .../AirbnbSwiftFormatTool/airbnb.swiftformat | 1 + 2 files changed, 37 insertions(+) diff --git a/README.md b/README.md index b635745..408515b 100644 --- a/README.md +++ b/README.md @@ -3601,6 +3601,42 @@ _You can enable the following settings in Xcode by running [this script](resourc +* (link) **Avoid defining properties that are then returned immediately.** Instead, return the value directly. [![SwiftFormat: redundantProperty](https://img.shields.io/badge/SwiftFormat-redundantProperty-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#redundantProperty) + +
+ + ### Why? + + Property declarations that are immediately returned are typically redundant and unnecessary. Sometimes these are unintentionally created as the byproduct of refactoring. Cleaning them up automatically simplifies the code. In some cases this also results in the `return` keyword itself being unnecessary, further simplifying the code. + + ```swift + // WRONG + var spaceship: Spaceship { + let spaceship = spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) + return spaceship + } + + // RIGHT + var spaceship: Spaceship { + spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) + } + + // WRONG + var spaceship: Spaceship { + let warpDrive = warpDriveBuilder.build() + let spaceship = spaceshipBuilder.build(warpDrive: warpDrive) + return spaceship + } + + // RIGHT + var spaceship: Spaceship { + let warpDrive = warpDriveBuilder.build() + return spaceshipBuilder.build(warpDrive: warpDrive) + } + ``` + +
+ **[⬆ back to top](#table-of-contents)** ## File Organization diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index 632cb02..84cc3b1 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -77,6 +77,7 @@ --rules redundantVoidReturnType --rules redundantOptionalBinding --rules redundantInternal +--rules redundantProperty --rules unusedArguments --rules spaceInsideBrackets --rules spaceInsideBraces From 95099d65cb22b031ce460ee0586874900e2a8c0b Mon Sep 17 00:00:00 2001 From: Denis Date: Thu, 20 Jun 2024 17:09:47 -0700 Subject: [PATCH 10/11] Update to latest SwiftLint version (#276) --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index f086696..e91e180 100644 --- a/Package.swift +++ b/Package.swift @@ -47,8 +47,8 @@ let package = Package( .binaryTarget( name: "SwiftLintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.53.0/SwiftLintBinary-macos.artifactbundle.zip", - checksum: "03416a4f75f023e10f9a76945806ddfe70ca06129b895455cc773c5c7d86b73e"), + url: "https://github.com/realm/SwiftLint/releases/download/0.55.1/SwiftLintBinary-macos.artifactbundle.zip", + checksum: "722a705de1cf4e0e07f2b7d2f9f631f3a8b2635a0c84cce99f9677b38aa4a1d6"), ]) // Emit an error on Linux, so Swift Package Manager's platform support detection doesn't say this package supports Linux From b23e5761ec7941929d9afbb059674f0702a41102 Mon Sep 17 00:00:00 2001 From: Rakuyo Date: Wed, 26 Jun 2024 14:03:21 +0800 Subject: [PATCH 11/11] feat: Update --- .github/workflows/main.yml | 3 +- Gemfile.lock | 6 +- README.md | 36 -- Rakefile | 8 +- .../RakuyoSwiftFormatTool/rakuyo.swiftformat | 3 +- .../RakuyoSwiftFormatToolTests.swift | 449 +++++++++--------- 6 files changed, 238 insertions(+), 267 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77768ad..268d49e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,6 @@ jobs: fail-fast: false matrix: xcode: - - '14.2' # Swift 5.7 - '14.3' # Swift 5.8 - '15.0' # Swift 5.9 steps: @@ -23,7 +22,7 @@ jobs: with: xcode: ${{ matrix.xcode }} - name: Test Package Plugin - run: bundle exec rake lint:swift + run: bundle exec rake swift:lint unit-tests: name: Unit Tests diff --git a/Gemfile.lock b/Gemfile.lock index 2ffc012..08f2be5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,17 +1,13 @@ -GEM - specs: - GEM remote: https://rubygems.org/ specs: rake (13.0.6) PLATFORMS - arm64-darwin-23 ruby DEPENDENCIES rake (~> 13.0.0)! BUNDLED WITH - 2.5.4 + 2.1.4 diff --git a/README.md b/README.md index c6e16ac..96b51a0 100644 --- a/README.md +++ b/README.md @@ -3581,42 +3581,6 @@ _You can enable the following settings in Xcode by running [this script](resourc -* (link) **Avoid defining properties that are then returned immediately.** Instead, return the value directly. [![SwiftFormat: redundantProperty](https://img.shields.io/badge/SwiftFormat-redundantProperty-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#redundantProperty) - -
- - ### Why? - - Property declarations that are immediately returned are typically redundant and unnecessary. Sometimes these are unintentionally created as the byproduct of refactoring. Cleaning them up automatically simplifies the code. In some cases this also results in the `return` keyword itself being unnecessary, further simplifying the code. - - ```swift - // WRONG - var spaceship: Spaceship { - let spaceship = spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) - return spaceship - } - - // RIGHT - var spaceship: Spaceship { - spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) - } - - // WRONG - var spaceship: Spaceship { - let warpDrive = warpDriveBuilder.build() - let spaceship = spaceshipBuilder.build(warpDrive: warpDrive) - return spaceship - } - - // RIGHT - var spaceship: Spaceship { - let warpDrive = warpDriveBuilder.build() - return spaceshipBuilder.build(warpDrive: warpDrive) - } - ``` - -
- **[⬆ back to top](#table-of-contents)** ## File Organization diff --git a/Rakefile b/Rakefile index b5a05bf..ed44930 100644 --- a/Rakefile +++ b/Rakefile @@ -3,16 +3,14 @@ require 'net/http' require 'json' require 'tempfile' -namespace :lint do +namespace :swift do desc 'Lints swift files' - task :swift do + task :lint do sh 'swift package --allow-writing-to-package-directory format --lint' end -end -namespace :format do desc 'Formats swift files' - task :swift do + task :format do sh 'swift package --allow-writing-to-package-directory format' end end diff --git a/Sources/RakuyoSwiftFormatTool/rakuyo.swiftformat b/Sources/RakuyoSwiftFormatTool/rakuyo.swiftformat index fb412f4..59a09d4 100644 --- a/Sources/RakuyoSwiftFormatTool/rakuyo.swiftformat +++ b/Sources/RakuyoSwiftFormatTool/rakuyo.swiftformat @@ -26,7 +26,7 @@ --wrapternary before-operators # wrap --extensionacl on-declarations # extensionAccessControl --patternlet inline # hoistPatternLet ---redundanttype inferred # redundantType, propertyType +--redundanttype inferred # redundantType --typeblanklines preserve # blankLinesAtStartOfScope, blankLinesAtEndOfScope --emptybraces spaced # emptyBraces --someAny disabled # opaqueGenericParameters @@ -73,7 +73,6 @@ --rules redundantVoidReturnType --rules redundantOptionalBinding --rules redundantInternal ---rules redundantProperty --rules unusedArguments --rules spaceInsideBrackets --rules spaceInsideBraces diff --git a/Tests/RakuyoSwiftFormatToolTests/RakuyoSwiftFormatToolTests.swift b/Tests/RakuyoSwiftFormatToolTests/RakuyoSwiftFormatToolTests.swift index c9967e5..716f8ae 100644 --- a/Tests/RakuyoSwiftFormatToolTests/RakuyoSwiftFormatToolTests.swift +++ b/Tests/RakuyoSwiftFormatToolTests/RakuyoSwiftFormatToolTests.swift @@ -7,245 +7,260 @@ import XCTest final class RakuyoSwiftFormatToolTest: XCTestCase { func testFormatWithNoViolations() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return EXIT_SUCCESS - }, - swiftLint: { - ranSwiftLint = true - return EXIT_SUCCESS - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true - return EXIT_SUCCESS - })) - - XCTAssertNil(error) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertTrue(ranSwiftLintAutocorrect) - } - - func testLintWithNoViolations() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - arguments: ["--lint"], - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return EXIT_SUCCESS - }, - swiftLint: { - ranSwiftLint = true - return EXIT_SUCCESS - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true - return EXIT_SUCCESS - })) - - XCTAssertNil(error) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - - // Should't run SwiftLint autocorrect in lint-only mode - XCTAssertFalse(ranSwiftLintAutocorrect) - } - - func testFormatWithViolations() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - - // When autocorrecting SwiftFormat returns EXIT_SUCCESS - // even if there were violations that were fixed - return EXIT_SUCCESS - }, - swiftLint: { - ranSwiftLint = true - return SwiftLintExitCode.lintFailure - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true - - // When autocorrecting SwiftLint returns EXIT_SUCCESS - // even if there were violations that were fixed - return EXIT_SUCCESS - })) - - XCTAssertEqual(error as? ExitCode, ExitCode(SwiftFormatExitCode.lintFailure)) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertTrue(ranSwiftLintAutocorrect) - } - - func testFormatWithOnlySwiftLintAutocorrectedViolation() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return EXIT_SUCCESS - }, - swiftLint: { - ranSwiftLint = true - - // Assume that the codebase has violations that would be corrected by SwiftLint autocorrect. - if ranSwiftLintAutocorrect { - // If SwiftLint autocorrect has already run, then there are no more violations. - // This is the expected behavior. - return EXIT_SUCCESS - } else { - // If SwiftLint autocorrect hasn't run yet, then there are still violations. - // This should not happen, because we run autocorrect first. - return SwiftLintExitCode.lintFailure - } - }, - swiftLintAutocorrect: { - // Assume that this SwiftLint autocorrect invocation applied a code change. - // In this case, SwiftLint still returns a zero exit code. - ranSwiftLintAutocorrect = true - return EXIT_SUCCESS - })) - - // Even though there was a SwiftLint failure, it was autocorrected so doesn't require attention. - // The tool should not return an error (e.g. it should return a zero exit code). - XCTAssertNil(error) - - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertTrue(ranSwiftLintAutocorrect) - } - - func testLintWithViolations() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - arguments: ["--lint"], - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return SwiftFormatExitCode.lintFailure - }, - swiftLint: { - ranSwiftLint = true - return SwiftLintExitCode.lintFailure - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true - return EXIT_SUCCESS - })) + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return EXIT_SUCCESS + }, + swiftLint: { + ranSwiftLint = true + return EXIT_SUCCESS + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + XCTAssertNil(error) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertTrue(ranSwiftLintAutocorrect) + } - XCTAssertEqual(error as? ExitCode, ExitCode.failure) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertFalse(ranSwiftLintAutocorrect) - } + func testLintWithNoViolations() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + arguments: ["--lint"], + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return EXIT_SUCCESS + }, + swiftLint: { + ranSwiftLint = true + return EXIT_SUCCESS + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + XCTAssertNil(error) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + + // Should't run SwiftLint autocorrect in lint-only mode + XCTAssertFalse(ranSwiftLintAutocorrect) + } - func testLintWithOnlySwiftLintViolation() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false + func testFormatWithViolations() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + + // When autocorrecting SwiftFormat returns EXIT_SUCCESS + // even if there were violations that were fixed + return EXIT_SUCCESS + }, + swiftLint: { + ranSwiftLint = true + return SwiftLintExitCode.lintFailure + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + + // When autocorrecting SwiftLint returns EXIT_SUCCESS + // even if there were violations that were fixed + return EXIT_SUCCESS + } + ) + ) + + XCTAssertEqual(error as? ExitCode, ExitCode(SwiftFormatExitCode.lintFailure)) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertTrue(ranSwiftLintAutocorrect) + } - let error = runFormatTool( - arguments: ["--lint"], - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return EXIT_SUCCESS - }, - swiftLint: { - ranSwiftLint = true - return SwiftLintExitCode.lintFailure - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true + func testFormatWithOnlySwiftLintAutocorrectedViolation() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return EXIT_SUCCESS + }, + swiftLint: { + ranSwiftLint = true + + // Assume that the codebase has violations that would be corrected by SwiftLint autocorrect. + if ranSwiftLintAutocorrect { + // If SwiftLint autocorrect has already run, then there are no more violations. + // This is the expected behavior. return EXIT_SUCCESS - })) + } + // If SwiftLint autocorrect hasn't run yet, then there are still violations. + // This should not happen, because we run autocorrect first. + return SwiftLintExitCode.lintFailure + }, + swiftLintAutocorrect: { + // Assume that this SwiftLint autocorrect invocation applied a code change. + // In this case, SwiftLint still returns a zero exit code. + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + // Even though there was a SwiftLint failure, it was autocorrected so doesn't require attention. + // The tool should not return an error (e.g. it should return a zero exit code). + XCTAssertNil(error) + + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertTrue(ranSwiftLintAutocorrect) + } - XCTAssertEqual(error as? ExitCode, ExitCode.failure) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertFalse(ranSwiftLintAutocorrect) - } + func testLintWithViolations() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + arguments: ["--lint"], + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return SwiftFormatExitCode.lintFailure + }, + swiftLint: { + ranSwiftLint = true + return SwiftLintExitCode.lintFailure + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + XCTAssertEqual(error as? ExitCode, ExitCode.failure) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertFalse(ranSwiftLintAutocorrect) + } - func testLintWithOnlySwiftFormatViolation() { - var ranSwiftFormat = false - var ranSwiftLint = false - var ranSwiftLintAutocorrect = false - - let error = runFormatTool( - arguments: ["--lint"], - with: MockCommands( - swiftFormat: { - ranSwiftFormat = true - return SwiftFormatExitCode.lintFailure - }, - swiftLint: { - ranSwiftLint = true - return EXIT_SUCCESS - }, - swiftLintAutocorrect: { - ranSwiftLintAutocorrect = true - return EXIT_SUCCESS - })) + func testLintWithOnlySwiftLintViolation() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + arguments: ["--lint"], + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return EXIT_SUCCESS + }, + swiftLint: { + ranSwiftLint = true + return SwiftLintExitCode.lintFailure + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + XCTAssertEqual(error as? ExitCode, ExitCode.failure) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertFalse(ranSwiftLintAutocorrect) + } - XCTAssertEqual(error as? ExitCode, ExitCode.failure) - XCTAssertTrue(ranSwiftFormat) - XCTAssertTrue(ranSwiftLint) - XCTAssertFalse(ranSwiftLintAutocorrect) - } + func testLintWithOnlySwiftFormatViolation() { + var ranSwiftFormat = false + var ranSwiftLint = false + var ranSwiftLintAutocorrect = false + + let error = runFormatTool( + arguments: ["--lint"], + with: MockCommands( + swiftFormat: { + ranSwiftFormat = true + return SwiftFormatExitCode.lintFailure + }, + swiftLint: { + ranSwiftLint = true + return EXIT_SUCCESS + }, + swiftLintAutocorrect: { + ranSwiftLintAutocorrect = true + return EXIT_SUCCESS + } + ) + ) + + XCTAssertEqual(error as? ExitCode, ExitCode.failure) + XCTAssertTrue(ranSwiftFormat) + XCTAssertTrue(ranSwiftLint) + XCTAssertFalse(ranSwiftLintAutocorrect) + } - func testHandlesUnexpectedErrorCode() { - let unexpectedSwiftFormatExitCode = runFormatTool( - with: MockCommands(swiftFormat: { 1234 })) + func testHandlesUnexpectedErrorCode() { + let unexpectedSwiftFormatExitCode = runFormatTool( + with: MockCommands(swiftFormat: { 1234 }) + ) - let unexpectedSwiftLintExitCode = runFormatTool( - with: MockCommands(swiftLint: { 42 })) + let unexpectedSwiftLintExitCode = runFormatTool( + with: MockCommands(swiftLint: { 42 }) + ) - XCTAssertEqual(unexpectedSwiftFormatExitCode as? ExitCode, ExitCode(1234)) - XCTAssertEqual(unexpectedSwiftLintExitCode as? ExitCode, ExitCode(42)) - } + XCTAssertEqual(unexpectedSwiftFormatExitCode as? ExitCode, ExitCode(1234)) + XCTAssertEqual(unexpectedSwiftLintExitCode as? ExitCode, ExitCode(42)) + } } // MARK: - Private extension RakuyoSwiftFormatToolTest { - /// Runs `AirbnbSwiftFormatTool` with the `Command` calls mocked using the given mocks + /// Runs `RakuyoSwiftFormatTool` with the `Command` calls mocked using the given mocks private func runFormatTool(arguments: [String]? = nil, with mocks: MockCommands) -> Error? { let existingRunCommandImplementation = Command.runCommand Command.runCommand = mocks.mockRunCommand(_:) defer { Command.runCommand = existingRunCommandImplementation } - let formatTool = try! AirbnbSwiftFormatTool.parse([ - "Sources", - "--swift-format-path", - "airbnb.swiftformat", - "--swift-lint-path", - "swiftlint.yml", - ] + (arguments ?? [])) - do { + let formatTool = try RakuyoSwiftFormatTool.parse([ + "Sources", + "--swift-format-path", + "rakuyo.swiftformat", + "--swift-lint-path", + "swiftlint.yml", + ] + (arguments ?? [])) + try formatTool.run() return nil } catch {