Skip to content

Add trailing comma support in cases missing from Swift 6.1 #81612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 22, 2025
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
12 changes: 9 additions & 3 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2137,7 +2137,7 @@ Parser::parseAttributeArguments(SourceLoc attrLoc, StringRef attrName,
}

return parseList(tok::r_paren, parensRange.Start, parensRange.End,
/*allow sep after last*/ true,
/*AllowSepAfterLast=*/true,
{diag::attr_expected_rparen, {attrName, isModifier}},
parseArg);
}
Expand Down Expand Up @@ -3245,7 +3245,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
StringRef AttrName = "@_originallyDefinedIn";
bool SuppressLaterDiags = false;
bool ParsedUnrecognizedPlatformName = false;
if (parseList(tok::r_paren, LeftLoc, RightLoc, false,
if (parseList(tok::r_paren, LeftLoc, RightLoc,
/*AllowSepAfterLast=*/false,
diag::originally_defined_in_missing_rparen,
[&]() -> ParserStatus {
SWIFT_DEFER {
Expand Down Expand Up @@ -4978,7 +4979,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
SourceLoc rParenLoc;
bool foundParamId = false;
status = parseList(
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast*/ false,
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast=*/false,
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
ParserStatus listStatus;
foundParamId = true;
Expand Down Expand Up @@ -9524,6 +9525,11 @@ ParserStatus Parser::parsePrimaryAssociatedTypeList(

// Parse the comma, if the list continues.
HasNextParam = consumeIf(tok::comma);

// The list ends if we find a trailing comma
if (startsWithGreater(Tok)) {
break;
}
} while (HasNextParam);

return Result;
Expand Down
9 changes: 7 additions & 2 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,10 @@ ParserStatus Parser::parseGenericArguments(SmallVectorImpl<TypeRepr *> &Args,
// Parse the comma, if the list continues.
if (!consumeIf(tok::comma))
break;

// If the comma was a trailing comma, finish parsing the list of types
if (startsWithGreater(Tok))
break;
}
}

Expand Down Expand Up @@ -1161,7 +1165,7 @@ ParserResult<TypeRepr> Parser::parseTypeTupleBody() {
SmallVector<TupleTypeReprElement, 8> ElementsR;

ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc,
/*AllowSepAfterLast=*/false,
/*AllowSepAfterLast=*/true,
diag::expected_rparen_tuple_type_list,
[&] () -> ParserStatus {
TupleTypeReprElement element;
Expand Down Expand Up @@ -1605,7 +1609,8 @@ bool Parser::canParseGenericArguments() {
if (!canParseType())
return false;
// Parse the comma, if the list continues.
} while (consumeIf(tok::comma));
// This could be the trailing comma.
} while (consumeIf(tok::comma) && !startsWithGreater(Tok));

if (!startsWithGreater(Tok)) {
return false;
Expand Down
5 changes: 4 additions & 1 deletion test/Casting/ParameterizedExistentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ struct GenericHolder<T>: Holder {
init(value: T) { self.value = value}
}

protocol PairType<T, U> {
protocol PairType<
T,
U,
> {
associatedtype T
associatedtype U

Expand Down
18 changes: 15 additions & 3 deletions test/Parse/trailing-comma.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ let values: [Int,] = [] // expected-note {{to match this opening '['}} expected-
// Tuple and Tuple Pattern

let _ = (a: 1, b: 2, c: 3,)
let _: (a: Int, b: Int, c: Int,) = (a: 1, b: 2, c: 3,)

// Closures
let _: (String, Int, Float,) -> Void

let (_, _,) = (0,1,)

Expand All @@ -34,7 +38,7 @@ struct S<T1, T2,> { }

func foo<T1, T2,>() { }

protocol P<T1, T2> {
protocol P<T1, T2,> {
associatedtype T1
associatedtype T2
}
Expand Down Expand Up @@ -98,7 +102,7 @@ struct Foo {

}

func f(in: @differentiable(reverse,) (Int) -> Int) { } // expected-warning {{@differentiable' has been renamed to '@differentiable(reverse)' and will be removed in the next release}} expected-error {{unexpected ',' separator}} expected-error {{expected ',' separator}} expected-error {{unnamed parameters must be written with the empty name '_'}}
func f(in: @differentiable(reverse,) (Int) -> Int) { } // expected-warning {{@differentiable' has been renamed to '@differentiable(reverse)' and will be removed in the next release}} expected-error {{expected ',' separator}} expected-error {{unnamed parameters must be written with the empty name '_'}}

@derivative(of: Self.other,) // expected-error {{unexpected ',' separator}}
func foo() {}
Expand Down Expand Up @@ -135,4 +139,12 @@ if true, { } // expected-error {{expected '{' after 'if' condition}}

guard true, else { } // expected-error {{expected expression in conditional}}

while true, { } // expected-error {{expected '{' after 'while' condition}}
while true, { } // expected-error {{expected '{' after 'while' condition}}

if #available(OSX 51,) { // expected-error {{expected platform name}}
}

@available(OSX 10.7, iOS 7.0, *,) // expected-error {{expected platform name}} expected-error {{expected declaration}}
@_originallyDefinedIn(module: "HighLevel", OSX 10.9, iOS 13.0,) // expected-error {{unexpected ',' separator}}
@backDeployed(before: OSX 10.9,) // expected-error {{unexpected ',' separator}}
public struct StructWithAvailability {}
13 changes: 13 additions & 0 deletions test/decl/ext/typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,16 @@ extension FooIntBarFloatDoubleInner {
}
}

struct Foo2<T1, T2, T3,> {}

typealias Bar2<
T1,
T2,
> = Foo2<
T1,
T2,
Bool,
>
Copy link
Member

@rintaro rintaro May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding test cases for generic arguments in expression positions to prevent future regressions? Like let _ = Generic<Int, Bool,>.self and let _ = Generic<Int, Bool,>(). It also uses parseGenericArguments() (currently) so it should work with your changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for remembering to check that! It didn't work originally, we also had to update the lookahead code that disambiguated the < between a generic list and a less than operator. Updated, working now :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah canParseGenericArguments(). That makes sense. Thank you for fixing it!


let _ = Foo2<Int, Bool, String,>.self
let _ = Bar2<Int, Bool,>()
15 changes: 15 additions & 0 deletions test/type/types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,18 @@ do {
subscript(_: inout Double...) -> Bool { true } // expected-error {{'inout' may only be used on function or initializer parameters}}
}
}

let tupleTypeWithTrailingComma: (
bar: String,
quux: String,
)

let _ = (bar: String, quux: String,).self

let closureTypeWithTrailingCommas: (
String,
String,
) -> (
bar: String,
quux: String,
)