diff --git a/Sources/DeclarationV1.swift b/Sources/DeclarationV1.swift index d8161b23..f1e6ea9b 100644 --- a/Sources/DeclarationV1.swift +++ b/Sources/DeclarationV1.swift @@ -1,5 +1,5 @@ // -// DeclarationHelpers.swift +// DeclarationV1.swift // SwiftFormat // // Created by Cal Stephens on 7/20/24. @@ -162,23 +162,9 @@ enum Declaration: Hashable { var isStoredProperty: Bool { guard keyword == "let" || keyword == "var" else { return false } - // If this property has a body, then it's a stored property - // if and only if the declaration body has a `didSet` or `willSet` keyword, - // based on the grammar for a variable declaration: - // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#grammar_variable-declaration let formatter = Formatter(tokens) - if let keywordIndex = formatter.index(of: .keyword(keyword), after: -1), - let startOfPropertyBody = formatter.startOfPropertyBody( - at: keywordIndex, - endOfPropertyIndex: formatter.tokens.count - ), - let nextToken = formatter.next(.nonSpaceOrCommentOrLinebreak, after: startOfPropertyBody) - { - return [.identifier("willSet"), .identifier("didSet")].contains(nextToken) - } - - // Otherwise, if the property doesn't have a body, then it must not be a computed property. - return true + guard let keywordIndex = formatter.index(of: .keyword(keyword), after: -1) else { return false } + return formatter.isStoredProperty(atIntroducerIndex: keywordIndex) } /// The original index of this declaration's primary keyword in the given formatter diff --git a/Sources/DeclarationV2.swift b/Sources/DeclarationV2.swift index f0b0e1b2..c452e846 100644 --- a/Sources/DeclarationV2.swift +++ b/Sources/DeclarationV2.swift @@ -70,7 +70,12 @@ extension DeclarationV2 { var name: String? { formatter.declarationName(keywordIndex: keywordIndex) } - + + /// A `Hashable` reference to this declaration. + var identity: AnyHashable { + ObjectIdentifier(self) + } + /// The child declarations of this declaration's body, if present. @_disfavoredOverload var body: [DeclarationV2]? { @@ -83,6 +88,45 @@ extension DeclarationV2 { return conditionalCompilation.body } } + + /// The modifiers before this declaration's keyword, including any attributes. + var modifiers: [String] { + var allModifiers = [String]() + _ = formatter.modifiersForDeclaration(at: keywordIndex, contains: { _, modifier in + allModifiers.append(modifier) + return false + }) + return allModifiers + } + + /// Whether or not this declaration represents a stored instance property + var isStoredInstanceProperty: Bool { + // A static property is not an instance property + !modifiers.contains("static") && isStoredProperty + } + + /// Whether or not this declaration represents a static stored property + var isStaticStoredProperty: Bool { + modifiers.contains("static") && isStoredProperty + } + + /// Whether or not this declaration represents a stored property + var isStoredProperty: Bool { + formatter.isStoredProperty(atIntroducerIndex: keywordIndex) + } + + /// Full information about this `let` or `var` property declaration. + var asPropertyDeclaration: Formatter.PropertyDeclaration? { + guard keyword == "let" || keyword == "var" else { return nil } + return formatter.parsePropertyDeclaration(atIntroducerIndex: keywordIndex) + } + + /// Removes this declaration from the source file. + /// After this point, this declaration reference is no longer valid. + func remove() { + formatter.unregisterDeclaration(self) + formatter.removeTokens(in: range) + } } /// A simple declaration without any child declarations, representing a property, function, etc. @@ -188,12 +232,27 @@ extension DeclarationV2 { /// Adds the given visibility keyword to the given declaration, /// replacing any existing visibility keyword. - func add(_ visibilityKeyword: Visibility) { + func addVisibility(_ visibilityKeyword: Visibility) { formatter.addDeclarationVisibility(visibilityKeyword, declarationKeywordIndex: keywordIndex) } /// Removes the given visibility keyword from the given declaration - func remove(_ visibilityKeyword: Visibility) { + func removeVisibility(_ visibilityKeyword: Visibility) { formatter.removeDeclarationVisibility(visibilityKeyword, declarationKeywordIndex: keywordIndex) } } + +/// We want to avoid including a Hashable requirement on DeclarationV2, +/// so instead you can use this container type if you need a Hashable declaration. +/// Uses reference identity of the `DeclarationV2` class value. +struct HashableDeclaration: Hashable { + let declaration: DeclarationV2 + + static func == (lhs: HashableDeclaration, rhs: HashableDeclaration) -> Bool { + lhs.declaration === rhs.declaration + } + + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(declaration)) + } +} diff --git a/Sources/Formatter.swift b/Sources/Formatter.swift index 2456d1fe..45e006cd 100644 --- a/Sources/Formatter.swift +++ b/Sources/Formatter.swift @@ -745,6 +745,18 @@ public extension Formatter { internal func registerDeclaration(_ declaration: DeclarationV2) { activeDeclarations.append(WeakDeclarationReference(declaration: declaration)) } + + /// Unregisters the given declaration and any children so it will no longer be notified of modifications. + internal func unregisterDeclaration(_ declaration: DeclarationV2) { + var declarationsToRemove = Set([declaration.identity]) + declaration.body?.forEachRecursiveDeclaration { childDeclaration in + declarationsToRemove.insert(childDeclaration.identity) + } + + activeDeclarations.removeAll(where: { declaration in + declarationsToRemove.contains(declaration.declaration?.identity) + }) + } } extension String { diff --git a/Sources/ParsingHelpers.swift b/Sources/ParsingHelpers.swift index e442cf47..1a8422d2 100644 --- a/Sources/ParsingHelpers.swift +++ b/Sources/ParsingHelpers.swift @@ -872,54 +872,20 @@ extension Formatter { /// Whether or not this property at the given introducer index (either `var` or `let`) /// is a stored property or a computed property. func isStoredProperty(atIntroducerIndex introducerIndex: Int) -> Bool { - assert(["let", "var"].contains(tokens[introducerIndex].string)) - - var parseIndex = introducerIndex - - // All properties have the property name after the introducer - if let propertyNameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: parseIndex), - tokens[propertyNameIndex].isIdentifierOrKeyword - { - parseIndex = propertyNameIndex + guard let property = parsePropertyDeclaration(atIntroducerIndex: introducerIndex) else { + return false } - // Properties have an optional `: TypeName` component - if let typeAnnotationStartIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: parseIndex), - tokens[typeAnnotationStartIndex] == .delimiter(":"), - let startOfTypeIndex = index(of: .nonSpaceOrComment, after: typeAnnotationStartIndex), - let typeRange = parseType(at: startOfTypeIndex)?.range - { - parseIndex = typeRange.upperBound - } - - // Properties have an optional `= expression` component - if let assignmentIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: parseIndex), - tokens[assignmentIndex] == .operator("=", .infix) - { - // If the type has an assignment operator, it's guaranteed to be a stored property. - return true - } - - // Finally, properties have an optional `{` body - if let startOfBody = index(of: .nonSpaceOrCommentOrLinebreak, after: parseIndex), - tokens[startOfBody] == .startOfScope("{") - { - // If this property has a body, then its a stored property if and only if the body - // has a `didSet` or `willSet` keyword, based on the grammar for a variable declaration. - if let nextToken = next(.nonSpaceOrCommentOrLinebreak, after: startOfBody), - [.identifier("willSet"), .identifier("didSet")].contains(nextToken) - { - return true - } else { - return false - } - } - - // If the property declaration isn't followed by a `{ ... }` block, - // then it's definitely a stored property and not a computed property. + // If this property doesn't have a body, then it's definitely a stored property. + guard let bodyRange = property.body?.range, + let firstTokenInBody = token(at: bodyRange.lowerBound) else { return true } + + // If this property has a body, then its a stored property if and only if the body + // has a `didSet` or `willSet` keyword, based on the grammar for a variable declaration. + return [.identifier("willSet"), .identifier("didSet")].contains(firstTokenInBody) } /// Determine if next line after this token should be indented @@ -1644,6 +1610,17 @@ extension Formatter { return startIndex ... endOfExpression } + /// Whether or not the body within this scope is a single expression + func scopeBodyIsSingleExpression(at startOfScopeIndex: Int) -> Bool { + guard let endOfScopeIndex = endOfScope(at: startOfScopeIndex), + startOfScopeIndex + 1 != endOfScopeIndex, + let firstTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, after: startOfScopeIndex + 1), + let expressionRange = parseExpressionRange(startingAt: firstTokenInBody, allowConditionalExpressions: true) + else { return false } + + return index(of: .nonSpaceOrCommentOrLinebreak, after: expressionRange.upperBound) == endOfScopeIndex + } + /// Whether or not the comment starting at the given index is a doc comment func isDocComment(startOfComment: Int) -> Bool { let commentToken = tokens[startOfComment] @@ -1688,22 +1665,43 @@ extension Formatter { } } - /// A property of the format `(let|var) identifier: Type = expression`. - /// - `: Type` and `= expression` elements are optional + /// A property of the format `(let|var) identifier: Type = expression { ... }`. + /// - `: Type`, `= expression`, and the following `{ ... }` body are optional struct PropertyDeclaration { + /// The start index for this propery's list of modifiers. + /// If there are no modifiers, `startOfModifiersIndex` is just `introducerIndex`. + let startOfModifiersIndex: Int + + /// The index of the `let` or `var` keyword let introducerIndex: Int + + /// The identifier / name of this propery. let identifier: String + + /// The index of this property's identifier / name. let identifierIndex: Int + + /// Information about the property's type definition, if written explicitly. let type: (colonIndex: Int, name: String, range: ClosedRange)? + + /// Information about the value following the propery's `=` token, if present. let value: (assignmentIndex: Int, expressionRange: ClosedRange)? + /// Information about the body following the property, which can include + /// `get`, `set`, `willSet`, `didSet` blocks, or just a single getter. + /// - `scopeRange` is the range starting at the `{` token and ending at the `}` token. + /// - `range` is the range of tokens inside the body scope. + let body: (scopeRange: ClosedRange, range: ClosedRange)? + var range: ClosedRange { - if let value = value { - return introducerIndex ... value.expressionRange.upperBound + if let bodyScopeRange = body?.scopeRange { + return startOfModifiersIndex ... bodyScopeRange.upperBound + } else if let value = value { + return startOfModifiersIndex ... value.expressionRange.upperBound } else if let type = type { - return introducerIndex ... type.range.upperBound + return startOfModifiersIndex ... type.range.upperBound } else { - return introducerIndex ... identifierIndex + return startOfModifiersIndex ... identifierIndex } } } @@ -1746,12 +1744,39 @@ extension Formatter { ) } + let endOfTypeOrIdentifierOrValue = valueInformation?.expressionRange.upperBound ?? endOfTypeOrIdentifier + var body: (scopeRange: ClosedRange, range: ClosedRange)? + + if let startOfBodyIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: endOfTypeOrIdentifierOrValue), + tokens[startOfBodyIndex] == .startOfScope("{"), + let endOfScope = endOfScope(at: startOfBodyIndex) + { + let bodyRange = startOfBodyIndex ... endOfScope + + let rangeInsideBody: ClosedRange + if startOfBodyIndex + 1 != endOfScope { + if let firstTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, after: startOfBodyIndex + 1), + let lastTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, before: endOfScope) + { + rangeInsideBody = firstTokenInBody ... lastTokenInBody + } else { + rangeInsideBody = startOfBodyIndex + 1 ... endOfScope - 1 + } + } else { + rangeInsideBody = bodyRange + } + + body = (bodyRange, rangeInsideBody) + } + return PropertyDeclaration( + startOfModifiersIndex: startOfModifiers(at: introducerIndex, includingAttributes: true), introducerIndex: introducerIndex, identifier: propertyIdentifier.string, identifierIndex: propertyIdentifierIndex, type: typeInformation, - value: valueInformation + value: valueInformation, + body: body ) } diff --git a/Sources/Rules/EnvironmentEntry.swift b/Sources/Rules/EnvironmentEntry.swift index 90f106b0..8a2b28d9 100644 --- a/Sources/Rules/EnvironmentEntry.swift +++ b/Sources/Rules/EnvironmentEntry.swift @@ -12,10 +12,10 @@ public extension FormatRule { // The @Entry macro is only available in Xcode 16 therefore this rule requires the same Xcode version to work. guard formatter.options.swiftVersion >= "6.0" else { return } - let declarations = formatter.parseDeclarations() + let declarations = formatter.parseDeclarationsV2() // Find all structs that conform to `EnvironmentKey` - let environmentKeys = Dictionary(uniqueKeysWithValues: formatter.findAllEnvironmentKeys(declarations).map { ($0.key, $0) }) + let environmentKeys = formatter.findAllEnvironmentKeys(declarations) // Find all `EnvironmentValues` properties let environmentValuesProperties = formatter.findAllEnvironmentValuesProperties(declarations, referencing: environmentKeys) @@ -24,8 +24,12 @@ public extension FormatRule { formatter.modifyEnvironmentValuesProperties(environmentValuesProperties) // Remove `EnvironmentKey`s - let updatedEnvironmentKeys = Set(environmentValuesProperties.map(\.key)) - formatter.removeEnvironmentKeys(updatedEnvironmentKeys) + for environmentValuesProperty in environmentValuesProperties { + if let environmentKey = environmentKeys[environmentValuesProperty.key] { + environmentKey.declaration.remove() + } + } + } examples: { """ ```diff @@ -49,95 +53,81 @@ public extension FormatRule { struct EnvironmentKey { let key: String - let declaration: Declaration - let defaultValueTokens: ArraySlice? - let isMultilineDefaultValue: Bool + let declaration: DeclarationV2 + let defaultValueTokens: [Token]? } struct EnvironmentValueProperty { let key: String let associatedEnvironmentKey: EnvironmentKey - let declaration: Declaration + let declaration: DeclarationV2 } extension Formatter { - func findAllEnvironmentKeys(_ declarations: [Declaration]) -> [EnvironmentKey] { - declarations.compactMap { declaration -> EnvironmentKey? in - guard declaration.keyword == "struct" || declaration.keyword == "enum", - declaration.openTokens.contains(.identifier("EnvironmentKey")), - let keyName = declaration.openTokens.first(where: \.isIdentifier), - let structDeclarationBody = declaration.body, - structDeclarationBody.count == 1, - let defaultValueDeclaration = structDeclarationBody.first(where: { + func findAllEnvironmentKeys(_ declarations: [DeclarationV2]) -> [String: EnvironmentKey] { + var environmentKeys = [String: EnvironmentKey]() + + for declaration in declarations { + guard let typeDeclaration = declaration as? TypeDeclaration, + typeDeclaration.keyword == "struct" || typeDeclaration.keyword == "enum", + typeDeclaration.conformances.contains(where: { $0.conformance == "EnvironmentKey" }), + let keyName = typeDeclaration.name, + typeDeclaration.body.count == 1, + let defaultValueDeclaration = typeDeclaration.body.first(where: { ($0.keyword == "var" || $0.keyword == "let") && $0.name == "defaultValue" - }), - let (defaultValueTokens, isMultiline) = findEnvironmentKeyDefaultValue(defaultValueDeclaration) - else { return nil } - return EnvironmentKey( - key: keyName.string, - declaration: declaration, - defaultValueTokens: defaultValueTokens, - isMultilineDefaultValue: isMultiline + }) + else { continue } + + environmentKeys[keyName] = EnvironmentKey( + key: keyName, + declaration: typeDeclaration, + defaultValueTokens: findEnvironmentKeyDefaultValue(defaultValueDeclaration) ) } + + return environmentKeys } - func findEnvironmentKeyDefaultValue(_ defaultValueDeclaration: Declaration) -> (tokens: ArraySlice?, isMultiline: Bool)? { - if defaultValueDeclaration.isStaticStoredProperty, - let equalsIndex = index(of: .operator("=", .infix), after: defaultValueDeclaration.originalRange.lowerBound), - equalsIndex <= defaultValueDeclaration.originalRange.upperBound, - let valueStartIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: equalsIndex), - let valueEndIndex = index(of: .nonSpaceOrCommentOrLinebreak, before: defaultValueDeclaration.originalRange.upperBound) - { - // Default value is stored property, not computed (e.g. static var defaultValue: Bool = false) - return (tokens[valueStartIndex ... valueEndIndex], false) - } else if let valueEndOfScopeIndex = endOfScope(at: defaultValueDeclaration.originalRange.upperBound - 1), - let valueStartOfScopeIndex = startOfScope(at: valueEndOfScopeIndex), - let valueStartIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: valueStartOfScopeIndex), - let valueEndIndex = index(of: .nonSpaceOrCommentOrLinebreak, before: valueEndOfScopeIndex) - { - let defaultValueDeclarations = parseDeclarations(in: valueStartIndex ... valueEndIndex) - let isMultilineDeclaration = defaultValueDeclarations.count > 1 - if defaultValueDeclarations.count <= 1 { - if defaultValueDeclarations.first?.name == "defaultValue" { - // Default value is implicitly `nil` (e.g. static var defaultValue: Bool?) - return (nil, false) - } else { - // Default value is a computed property with a single value (e.g. static var defaultValue: Bool { false }) - return (tokens[valueStartIndex ... valueEndIndex], isMultilineDeclaration) - } + func findEnvironmentKeyDefaultValue(_ defaultValueDeclaration: DeclarationV2) -> [Token]? { + guard let property = defaultValueDeclaration.asPropertyDeclaration else { return nil } + + if let valueRange = property.value?.expressionRange { + return Array(tokens[valueRange]) + } + + else if let body = property.body { + // If the body contains multiple expressions, the final output will need to be wrapped + // in an immediately-executed closure. + if !scopeBodyIsSingleExpression(at: body.scopeRange.lowerBound) { + let existingBodyScope = Array(tokens[body.scopeRange]) + return existingBodyScope + [.startOfScope("("), .endOfScope(")")] } else { - // Default value is a multiline computed property: - // ``` - // static var defaultValue: Bool { - // let computedValue = compute() - // return computedValue - // } - // ``` - return (tokens[valueStartOfScopeIndex ... valueEndOfScopeIndex], isMultilineDeclaration) + return Array(tokens[body.range]) } - } else { return nil } + } + + return nil } - func findAllEnvironmentValuesProperties(_ declarations: [Declaration], referencing environmentKeys: [String: EnvironmentKey]) - -> [EnvironmentValueProperty] - { + func findAllEnvironmentValuesProperties( + _ declarations: [DeclarationV2], + referencing environmentKeys: [String: EnvironmentKey] + ) -> [EnvironmentValueProperty] { declarations .filter { - $0.keyword == "extension" && $0.openTokens.contains(.identifier("EnvironmentValues")) + $0.keyword == "extension" && $0.name == "EnvironmentValues" }.compactMap { environmentValuesDeclaration -> [EnvironmentValueProperty]? in environmentValuesDeclaration.body?.compactMap { propertyDeclaration -> (EnvironmentValueProperty)? in - guard propertyDeclaration.isSimpleDeclaration, - propertyDeclaration.keyword == "var", + guard propertyDeclaration.keyword == "var", let key = propertyDeclaration.tokens.first(where: { environmentKeys[$0.string] != nil })?.string, let environmentKey = environmentKeys[key] else { return nil } // Ensure the property has a setter and a getter, this can avoid edge cases where // a property references a `EnvironmentKey` and consumes it to perform some computation. - let propertyFormatter = Formatter(propertyDeclaration.tokens) - guard let indexOfSetter = propertyDeclaration.tokens.firstIndex(where: { $0 == .identifier("set") }), - propertyFormatter.isAccessorKeyword(at: indexOfSetter) + guard let bodyRange = propertyDeclaration.asPropertyDeclaration?.body?.range, + let indexOfSetter = index(of: .identifier("set"), in: Range(bodyRange)), + isAccessorKeyword(at: indexOfSetter) else { return nil } return EnvironmentValueProperty( @@ -150,46 +140,25 @@ extension Formatter { } func modifyEnvironmentValuesProperties(_ environmentValuesPropertiesDeclarations: [EnvironmentValueProperty]) { - // Loop the collection in reverse to avoid invalidating the declaration indexes as we modify the property - for envProperty in environmentValuesPropertiesDeclarations.reversed() { - let propertyDeclaration = envProperty.declaration - guard let propertyBodyStartIndex = index(of: .startOfScope("{"), after: propertyDeclaration.originalRange.lowerBound), - let propertyBodyEndIndex = endOfScope(at: propertyBodyStartIndex), - let propertyStartIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: propertyDeclaration.originalRange.lowerBound) - else { - continue - } + for envProperty in environmentValuesPropertiesDeclarations { + guard let propertyDeclaration = envProperty.declaration.asPropertyDeclaration, + let bodyScopeRange = propertyDeclaration.body?.scopeRange + else { continue } + // Remove `EnvironmentValues.property` getter and setters - if let nonSpaceTokenIndexBeforeBody = index(of: .nonSpaceOrLinebreak, before: propertyBodyStartIndex), nonSpaceTokenIndexBeforeBody != propertyBodyStartIndex { + if let nonSpaceTokenIndexBeforeBody = index(of: .nonSpaceOrLinebreak, before: bodyScopeRange.lowerBound), nonSpaceTokenIndexBeforeBody != bodyScopeRange.lowerBound { // There are some spaces between the property body and the property type definition, we should remove the extra spaces. - let propertyBodyStartIndex = nonSpaceTokenIndexBeforeBody + 1 - removeTokens(in: propertyBodyStartIndex ... propertyBodyEndIndex) + removeTokens(in: nonSpaceTokenIndexBeforeBody + 1 ... bodyScopeRange.upperBound) } else { - removeTokens(in: propertyBodyStartIndex ... propertyBodyEndIndex) + removeTokens(in: bodyScopeRange) } // Add `EnvironmentKey.defaultValue` to `EnvironmentValues property` if let defaultValueTokens = envProperty.associatedEnvironmentKey.defaultValueTokens { - var defaultValueTokens = [.space(" "), .operator("=", .infix), .space(" ")] + defaultValueTokens - - if envProperty.associatedEnvironmentKey.isMultilineDefaultValue { - defaultValueTokens.append(contentsOf: [.endOfScope("("), .endOfScope(")")]) - } - insert(defaultValueTokens, at: endOfLine(at: propertyStartIndex)) + let defaultValueTokens = [.space(" "), .operator("=", .infix), .space(" ")] + defaultValueTokens + insert(defaultValueTokens, at: endOfLine(at: propertyDeclaration.range.lowerBound)) } // Add @Entry Macro - insert([.identifier("@Entry"), .space(" ")], at: propertyStartIndex) - } - } - - func removeEnvironmentKeys(_ updatedEnvironmentKeys: Set) { - guard !updatedEnvironmentKeys.isEmpty else { return } - - // After modifying the EnvironmentValues properties, parse declarations again to delete the Environment keys in their new position. - let repositionedEnvironmentKeys = findAllEnvironmentKeys(parseDeclarations()) - - // Loop the collection in reverse to avoid invalidating the declaration indexes as we remove EnvironmentKey - for declaration in repositionedEnvironmentKeys.reversed() where updatedEnvironmentKeys.contains(declaration.key) { - removeTokens(in: declaration.declaration.originalRange) + insert([.identifier("@Entry"), .space(" ")], at: propertyDeclaration.range.lowerBound) } } } diff --git a/Sources/Rules/ExtensionAccessControl.swift b/Sources/Rules/ExtensionAccessControl.swift index 975ecd59..fb81172b 100644 --- a/Sources/Rules/ExtensionAccessControl.swift +++ b/Sources/Rules/ExtensionAccessControl.swift @@ -74,7 +74,7 @@ public extension FormatRule { if memberVisibility != extensionVisibility, !(memberVisibility == .internal && visibilityKeyword == nil) { - extensionDeclaration.add(memberVisibility) + extensionDeclaration.addVisibility(memberVisibility) } extensionDeclaration.body.forEachRecursiveDeclarationExcludingTypeBodies { bodyDeclaration in @@ -82,11 +82,11 @@ public extension FormatRule { let visibility = bodyDeclaration.visibility() if memberVisibility > visibility ?? extensionVisibility ?? .internal { if visibility == nil { - bodyDeclaration.add(.internal) + bodyDeclaration.addVisibility(.internal) } return } - bodyDeclaration.remove(memberVisibility) + bodyDeclaration.removeVisibility(memberVisibility) } // Move the extension's visibility keyword to each individual declaration @@ -95,7 +95,7 @@ public extension FormatRule { guard let extensionVisibility = extensionVisibility else { return } // Remove the visibility keyword from the extension declaration itself - extensionDeclaration.remove(visibilityKeyword!) + extensionDeclaration.removeVisibility(visibilityKeyword!) // And apply the extension's visibility to each of its child declarations // that don't have an explicit visibility keyword @@ -103,7 +103,7 @@ public extension FormatRule { if bodyDeclaration.visibility() == nil { // If there was no explicit visibility keyword, then this declaration // was using the visibility of the extension itself. - bodyDeclaration.add(extensionVisibility) + bodyDeclaration.addVisibility(extensionVisibility) } } }