Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 15 additions & 17 deletions Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
// Prioritize keeping "<modifiers> macro <name>(" together. Also include the ")" if the
// parameter list is empty.
let firstTokenAfterAttributes =
node.modifiers?.firstToken(viewMode: .sourceAccurate) ?? node.macroKeyword
node.modifiers.firstToken(viewMode: .sourceAccurate) ?? node.macroKeyword
before(firstTokenAfterAttributes, tokens: .open)
after(node.macroKeyword, tokens: .break)
if hasArguments || node.genericParameterClause != nil {
Expand Down Expand Up @@ -352,7 +352,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {

// Prioritize keeping "<modifiers> func <name>(" together. Also include the ")" if the parameter
// list is empty.
let firstTokenAfterAttributes = node.modifiers?.firstToken(viewMode: .sourceAccurate) ?? node.funcKeyword
let firstTokenAfterAttributes = node.modifiers.firstToken(viewMode: .sourceAccurate) ?? node.funcKeyword
before(firstTokenAfterAttributes, tokens: .open)
after(node.funcKeyword, tokens: .break)
if hasArguments || node.genericParameterClause != nil {
Expand Down Expand Up @@ -392,7 +392,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
arrangeParameterClause(node.signature.parameterClause, forcesBreakBeforeRightParen: node.body != nil)

// Prioritize keeping "<modifiers> init<punctuation>" together.
let firstTokenAfterAttributes = node.modifiers?.firstToken(viewMode: .sourceAccurate) ?? node.initKeyword
let firstTokenAfterAttributes = node.modifiers.firstToken(viewMode: .sourceAccurate) ?? node.initKeyword
before(firstTokenAfterAttributes, tokens: .open)

if hasArguments || node.genericParameterClause != nil {
Expand Down Expand Up @@ -427,7 +427,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)

// Prioritize keeping "<modifiers> subscript" together.
if let firstModifierToken = node.modifiers?.firstToken(viewMode: .sourceAccurate) {
if let firstModifierToken = node.modifiers.firstToken(viewMode: .sourceAccurate) {
before(firstModifierToken, tokens: .open)

if hasArguments || node.genericParameterClause != nil {
Expand Down Expand Up @@ -700,18 +700,16 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
? Token.break(.same, newlines: .soft) : Token.space
before(node.catchKeyword, tokens: catchPrecedingBreak)

if let catchItems = node.catchItems {
// If there are multiple items in the `catch` clause, wrap each in open/close breaks so that
// their internal breaks stack correctly. Otherwise, if there is only a single clause, use the
// old (pre-SE-0276) behavior (a fixed space after the `catch` keyword).
if catchItems.count > 1 {
for catchItem in catchItems {
before(catchItem.firstToken(viewMode: .sourceAccurate), tokens: .break(.open(kind: .continuation)))
after(catchItem.lastToken(viewMode: .sourceAccurate), tokens: .break(.close(mustBreak: false), size: 0))
}
} else {
before(node.catchItems?.firstToken(viewMode: .sourceAccurate), tokens: .space)
// If there are multiple items in the `catch` clause, wrap each in open/close breaks so that
// their internal breaks stack correctly. Otherwise, if there is only a single clause, use the
// old (pre-SE-0276) behavior (a fixed space after the `catch` keyword).
if node.catchItems.count > 1 {
for catchItem in node.catchItems {
before(catchItem.firstToken(viewMode: .sourceAccurate), tokens: .break(.open(kind: .continuation)))
after(catchItem.lastToken(viewMode: .sourceAccurate), tokens: .break(.close(mustBreak: false), size: 0))
}
} else {
before(node.catchItems.firstToken(viewMode: .sourceAccurate), tokens: .space)
}

arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements)
Expand Down Expand Up @@ -1023,11 +1021,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
preVisitInsertingContextualBreaks(node)

// If there are multiple trailing closures, force all the closures in the call to break.
if let additionalTrailingClosures = node.additionalTrailingClosures {
if !node.additionalTrailingClosures.isEmpty {
if let closure = node.trailingClosure {
forcedBreakingClosures.insert(closure.id)
}
for additionalTrailingClosure in additionalTrailingClosures {
for additionalTrailingClosure in node.additionalTrailingClosures {
forcedBreakingClosures.insert(additionalTrailingClosure.closure.id)
}
}
Expand Down
92 changes: 52 additions & 40 deletions Sources/SwiftFormatRules/AddModifierRewriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,124 +23,136 @@ fileprivate final class AddModifierRewriter: SyntaxRewriter {
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.bindingSpecifier)
return DeclSyntax(result)
}
var node = node

// If variable already has an accessor keyword, skip (do not overwrite)
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }

// Put accessor keyword before the first modifier keyword in the declaration
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.funcKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: AssociatedTypeDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.associatedtypeKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.classKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.enumKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {

guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.protocolKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: StructDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.structKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: TypeAliasDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.typealiasKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: InitializerDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.initKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax {
// Check for modifiers, and, if none, insert the modifier and relocate trivia from the displaced
// token.
guard var modifiers = node.modifiers else {
guard !node.modifiers.isEmpty else {
let result = setOnlyModifier(in: node, keywordKeypath: \.subscriptKeyword)
return DeclSyntax(result)
}
guard modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
modifiers.triviaPreservingInsert(modifierKeyword, at: modifiers.startIndex)
return DeclSyntax(node.with(\.modifiers, modifiers))
guard node.modifiers.accessLevelModifier == nil else { return DeclSyntax(node) }
var node = node
node.modifiers.triviaPreservingInsert(modifierKeyword, at: node.modifiers.startIndex)
return DeclSyntax(node)
}

/// Moves trivia in the given node to correct the placement of potentially displaced trivia in the
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public final class AlwaysUseLowerCamelCase: SyntaxLintRule {
// Don't diagnose any issues when the variable is overriding, because this declaration can't
// rename the variable. If the user analyzes the code where the variable is really declared,
// then the diagnostic can be raised for just that location.
if let modifiers = node.modifiers, modifiers.has(modifier: "override") {
if node.modifiers.has(modifier: "override") {
return .visitChildren
}

Expand Down Expand Up @@ -114,7 +114,7 @@ public final class AlwaysUseLowerCamelCase: SyntaxLintRule {
// Don't diagnose any issues when the function is overriding, because this declaration can't
// rename the function. If the user analyzes the code where the function is really declared,
// then the diagnostic can be raised for just that location.
if let modifiers = node.modifiers, modifiers.has(modifier: "override") {
if node.modifiers.has(modifier: "override") {
return .visitChildren
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public final class AmbiguousTrailingClosureOverload: SyntaxLintRule {
let params = fn.signature.parameterClause.parameters
guard let firstParam = params.firstAndOnly else { continue }
guard firstParam.type.is(FunctionTypeSyntax.self) else { continue }
if let mods = fn.modifiers, mods.has(modifier: "static") || mods.has(modifier: "class") {
if fn.modifiers.has(modifier: "static") || fn.modifiers.has(modifier: "class") {
staticOverloads[fn.name.text, default: []].append(fn)
} else {
overloads[fn.name.text, default: []].append(fn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ public final class DontRepeatTypeInStaticProperties: SyntaxLintRule {
for member in members {
guard
let varDecl = member.decl.as(VariableDeclSyntax.self),
let modifiers = varDecl.modifiers,
modifiers.has(modifier: "static") || modifiers.has(modifier: "class")
varDecl.modifiers.has(modifier: "static") || varDecl.modifiers.has(modifier: "class")
else { continue }

let bareTypeName = removingPossibleNamespacePrefix(from: typeName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public final class FileScopedDeclarationPrivacy: SyntaxFormatRule {
private func rewrittenDecl<DeclType: DeclSyntaxProtocol>(
_ decl: DeclType,
modifiers: DeclModifierListSyntax?,
factory: (DeclModifierListSyntax?) -> DeclType
factory: (DeclModifierListSyntax) -> DeclType
) -> DeclType {
let invalidAccess: TokenKind
let validAccess: TokenKind
Expand Down
20 changes: 9 additions & 11 deletions Sources/SwiftFormatRules/FullyIndirectEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public final class FullyIndirectEnum: SyntaxFormatRule {

public override func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
let enumMembers = node.memberBlock.members
guard let enumModifiers = node.modifiers,
!enumModifiers.has(modifier: "indirect"),
guard !node.modifiers.has(modifier: "indirect"),
allCasesAreIndirect(in: enumMembers)
else {
return DeclSyntax(node)
Expand All @@ -37,14 +36,13 @@ public final class FullyIndirectEnum: SyntaxFormatRule {
let newMembers = enumMembers.map {
(member: MemberBlockItemSyntax) -> MemberBlockItemSyntax in
guard let caseMember = member.decl.as(EnumCaseDeclSyntax.self),
let modifiers = caseMember.modifiers,
modifiers.has(modifier: "indirect"),
let firstModifier = modifiers.first
caseMember.modifiers.has(modifier: "indirect"),
let firstModifier = caseMember.modifiers.first
else {
return member
}

let newCase = caseMember.with(\.modifiers, modifiers.remove(name: "indirect"))
let newCase = caseMember.with(\.modifiers, caseMember.modifiers.remove(name: "indirect"))
let formattedCase = rearrangeLeadingTrivia(firstModifier.leadingTrivia, on: newCase)
return member.with(\.decl, DeclSyntax(formattedCase))
}
Expand All @@ -70,7 +68,7 @@ public final class FullyIndirectEnum: SyntaxFormatRule {
let newMemberBlock = node.memberBlock.with(\.members, MemberBlockItemListSyntax(newMembers))
return DeclSyntax(
newEnumDecl
.with(\.modifiers, (newEnumDecl.modifiers ?? DeclModifierListSyntax([])) + [newModifier])
.with(\.modifiers, newEnumDecl.modifiers + [newModifier])
.with(\.memberBlock, newMemberBlock))
}

Expand All @@ -82,7 +80,7 @@ public final class FullyIndirectEnum: SyntaxFormatRule {
for member in members {
if let caseMember = member.decl.as(EnumCaseDeclSyntax.self) {
hadCases = true
guard let modifiers = caseMember.modifiers, modifiers.has(modifier: "indirect") else {
guard caseMember.modifiers.has(modifier: "indirect") else {
return false
}
}
Expand All @@ -97,11 +95,11 @@ public final class FullyIndirectEnum: SyntaxFormatRule {
) -> EnumCaseDeclSyntax {
var formattedCase = enumCaseDecl

if var modifiers = formattedCase.modifiers, var firstModifier = modifiers.first {
if var firstModifier = formattedCase.modifiers.first {
// If the case has modifiers, attach the leading trivia to the first one.
firstModifier.leadingTrivia = leadingTrivia
modifiers[modifiers.startIndex] = firstModifier
formattedCase.modifiers = modifiers
formattedCase.modifiers[formattedCase.modifiers.startIndex] = firstModifier
formattedCase.modifiers = formattedCase.modifiers
} else {
// Otherwise, attach the trivia to the `case` keyword itself.
formattedCase.caseKeyword.leadingTrivia = leadingTrivia
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ public final class NeverUseImplicitlyUnwrappedOptionals: SyntaxLintRule {
public override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
guard context.importsXCTest == .doesNotImportXCTest else { return .skipChildren }
// Ignores IBOutlet variables
if let attributes = node.attributes {
for attribute in attributes {
if (attribute.as(AttributeSyntax.self))?.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "IBOutlet" {
return .skipChildren
}
for attribute in node.attributes {
if (attribute.as(AttributeSyntax.self))?.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "IBOutlet" {
return .skipChildren
}
}
// Finds type annotation for variable(s)
Expand Down
Loading