Skip to content

Commit

Permalink
Merge pull request #623 from allevato/5.9-features-in-5.9
Browse files Browse the repository at this point in the history
[5.9 🍒] Add support for various new 5.9 features.
  • Loading branch information
allevato committed Sep 15, 2023
2 parents f4103e3 + 89c7073 commit 83248b4
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 5 deletions.
Expand Up @@ -17,7 +17,8 @@ extension DeclModifierListSyntax {
var accessLevelModifier: DeclModifierSyntax? {
for modifier in self {
switch modifier.name.tokenKind {
case .keyword(.public), .keyword(.private), .keyword(.fileprivate), .keyword(.internal):
case .keyword(.public), .keyword(.private), .keyword(.fileprivate), .keyword(.internal),
.keyword(.package):
return modifier
default:
continue
Expand Down
44 changes: 44 additions & 0 deletions Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift
Expand Up @@ -2293,6 +2293,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {

override func visit(_ node: GenericParameterSyntax) -> SyntaxVisitorContinueKind {
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
after(node.eachKeyword, tokens: .break)
after(node.colon, tokens: .break)
if let trailingComma = node.trailingComma {
after(trailingComma, tokens: .close, .break(.same))
Expand All @@ -2312,6 +2313,28 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
return .visitChildren
}

override func visit(_ node: PackElementExprSyntax) -> SyntaxVisitorContinueKind {
// `each` cannot be separated from the following token, or it is parsed as an identifier itself.
after(node.eachKeyword, tokens: .space)
return .visitChildren
}

override func visit(_ node: PackElementTypeSyntax) -> SyntaxVisitorContinueKind {
// `each` cannot be separated from the following token, or it is parsed as an identifier itself.
after(node.eachKeyword, tokens: .space)
return .visitChildren
}

override func visit(_ node: PackExpansionExprSyntax) -> SyntaxVisitorContinueKind {
after(node.repeatKeyword, tokens: .break)
return .visitChildren
}

override func visit(_ node: PackExpansionTypeSyntax) -> SyntaxVisitorContinueKind {
after(node.repeatKeyword, tokens: .break)
return .visitChildren
}

override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
return .visitChildren
}
Expand Down Expand Up @@ -2481,6 +2504,27 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
return .visitChildren
}

override func visit(_ node: ConsumeExprSyntax) -> SyntaxVisitorContinueKind {
// The `consume` keyword cannot be separated from the following token or it will be parsed as
// an identifier.
after(node.consumeKeyword, tokens: .space)
return .visitChildren
}

override func visit(_ node: CopyExprSyntax) -> SyntaxVisitorContinueKind {
// The `copy` keyword cannot be separated from the following token or it will be parsed as an
// identifier.
after(node.copyKeyword, tokens: .space)
return .visitChildren
}

override func visit(_ node: DiscardStmtSyntax) -> SyntaxVisitorContinueKind {
// The `discard` keyword cannot be separated from the following token or it will be parsed as
// an identifier.
after(node.discardKeyword, tokens: .space)
return .visitChildren
}

override func visit(_ node: InheritanceClauseSyntax) -> SyntaxVisitorContinueKind {
// Normally, the open-break is placed before the open token. In this case, it's intentionally
// ordered differently so that the inheritance list can start on the current line and only
Expand Down
Expand Up @@ -166,8 +166,7 @@ public final class AlwaysUseLiteralForEmptyCollectionInit : SyntaxFormatRule {
}

private func getLiteralType(_ arrayLiteral: ArrayExprSyntax) -> TypeSyntax? {
guard let elementExpr = arrayLiteral.elements.firstAndOnly,
elementExpr.is(ArrayElementSyntax.self) else {
guard arrayLiteral.elements.count == 1 else {
return nil
}

Expand Down
Expand Up @@ -32,8 +32,8 @@ public final class NoAccessLevelOnExtensionDeclaration: SyntaxFormatRule {
var result = node

switch keyword {
// Public, private, or fileprivate keywords need to be moved to members
case .public, .private, .fileprivate:
// Public, private, fileprivate, or package keywords need to be moved to members
case .public, .private, .fileprivate, .package:
// The effective access level of the members of a `private` extension is `fileprivate`, so
// we have to update the keyword to ensure that the result is correct.
var accessKeywordToAdd = accessKeyword
Expand Down
14 changes: 14 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/ConsumeExprTests.swift
@@ -0,0 +1,14 @@
final class ConsumeExprTests: PrettyPrintTestCase {
func testConsume() {
assertPrettyPrintEqual(
input: """
let x = consume y
""",
expected: """
let x =
consume y
""",
linelength: 16)
}
}
14 changes: 14 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/CopyExprSyntax.swift
@@ -0,0 +1,14 @@
final class CopyExprTests: PrettyPrintTestCase {
func testCopy() {
assertPrettyPrintEqual(
input: """
let x = copy y
""",
expected: """
let x =
copy y
""",
linelength: 13)
}
}
13 changes: 13 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/DiscardStmtTests.swift
@@ -0,0 +1,13 @@
final class DiscardStmtTests: PrettyPrintTestCase {
func testDiscard() {
assertPrettyPrintEqual(
input: """
discard self
""",
expected: """
discard self
""",
linelength: 9)
}
}
46 changes: 46 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/ParameterPackTests.swift
@@ -0,0 +1,46 @@
final class ParameterPackTests: PrettyPrintTestCase {
func testGenericPackArgument() {
assertPrettyPrintEqual(
input: """
func someFunction<each P>() {}
struct SomeStruct<each P> {}
""",
expected: """
func someFunction<
each P
>() {}
struct SomeStruct<
each P
> {}
""",
linelength: 22)
}

func testPackExpansionsAndElements() {
assertPrettyPrintEqual(
input: """
repeat checkNilness(of: each value)
""",
expected: """
repeat checkNilness(
of: each value)
""",
linelength: 25)

assertPrettyPrintEqual(
input: """
repeat f(of: each v)
""",
expected: """
repeat
f(
of:
each v
)
""",
linelength: 7)
}
}
Expand Up @@ -128,6 +128,31 @@ final class NoAccessLevelOnExtensionDeclarationTests: LintOrFormatRuleTestCase {
)
}

func testPackageAccessLevel() {
assertFormatting(
NoAccessLevelOnExtensionDeclaration.self,
input: """
1️⃣package extension Foo {
2️⃣func f() {}
}
""",
expected: """
extension Foo {
package func f() {}
}
""",
findings: [
FindingSpec(
"1️⃣",
message: "move this 'package' access modifier to precede each member inside this extension",
notes: [
NoteSpec("2️⃣", message: "add 'package' access modifier to this declaration"),
]
),
]
)
}

func testPrivateIsEffectivelyFileprivate() {
assertFormatting(
NoAccessLevelOnExtensionDeclaration.self,
Expand Down

0 comments on commit 83248b4

Please sign in to comment.