Skip to content

Commit

Permalink
Add rule to infer property types from the right-hand-side value rathe…
Browse files Browse the repository at this point in the history
…r than writing the type explicitly on the left-hand side (#263)
  • Loading branch information
calda authored Jun 7, 2024
1 parent 8b2776f commit 2c1c76a
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 20 deletions.
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
120 changes: 110 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```

</details>

* <a id='infer-property-types'></a>(<a href='#infer-property-types'>link</a>) **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)

<details>

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
```

</details>
Expand Down
17 changes: 9 additions & 8 deletions Sources/AirbnbSwiftFormatTool/airbnb.swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -102,4 +102,5 @@
--rules wrapMultilineConditionalAssignment
--rules blankLineAfterMultilineSwitchCase
--rules consistentSwitchStatementSpacing
--rules semicolons
--rules semicolons
--rules propertyType

0 comments on commit 2c1c76a

Please sign in to comment.