Skip to content

Commit 5c28f1d

Browse files
authored
Merge pull request #78313 from beccadax/llvm_reachable
2 parents e38c3a1 + 90f7724 commit 5c28f1d

File tree

5 files changed

+106
-31
lines changed

5 files changed

+106
-31
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ IDENTIFIER(AsyncIterator)
124124
IDENTIFIER(load)
125125
IDENTIFIER(main)
126126
IDENTIFIER_WITH_NAME(MainEntryPoint, "$main")
127+
IDENTIFIER(message)
127128
IDENTIFIER(next)
128129
IDENTIFIER_(nsErrorDomain)
129130
IDENTIFIER(objectAtIndexedSubscript)

include/swift/Parse/Parser.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1789,7 +1789,10 @@ class Parser {
17891789
/// \param name The parsed name of the label (empty if it doesn't exist, or is
17901790
/// _)
17911791
/// \param loc The location of the label (empty if it doesn't exist)
1792-
void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc);
1792+
/// \param isAttr True if this is an argument label for an attribute (allows, but deprecates, use of
1793+
/// \c '=' instead of \c ':').
1794+
void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc,
1795+
bool isAttr = false);
17931796

17941797
enum class DeclNameFlag : uint8_t {
17951798
/// If passed, operator basenames are allowed.

lib/Parse/ParseDecl.cpp

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3597,35 +3597,60 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
35973597
case DeclAttrKind::UnavailableFromAsync: {
35983598
StringRef message;
35993599
if (consumeIfAttributeLParen()) {
3600-
if (!Tok.is(tok::identifier)) {
3601-
llvm_unreachable("Flag must start with an identifier");
3602-
}
3603-
3604-
StringRef flag = Tok.getText();
3600+
auto tokMayBeArgument = [&]() -> bool {
3601+
return Tok.isNot(tok::r_paren, tok::comma) &&
3602+
!isKeywordPossibleDeclStart(Context.LangOpts, Tok);
3603+
};
36053604

3606-
if (flag != "message") {
3607-
diagnose(Tok.getLoc(), diag::attr_unknown_option, flag, AttrName);
3608-
return makeParserError();
3609-
}
3610-
consumeToken();
3611-
if (!consumeIf(tok::colon)) {
3612-
if (!Tok.is(tok::equal)) {
3613-
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, flag);
3614-
return makeParserSuccess();
3605+
Identifier label;
3606+
SourceLoc labelLoc;
3607+
parseOptionalArgumentLabel(label, labelLoc, /*isAttr=*/true);
3608+
3609+
if (label.empty()) {
3610+
// If we have the identifier 'message', assume the user forgot the
3611+
// colon.
3612+
if (Tok.isContextualKeyword("message")) {
3613+
labelLoc = consumeToken();
3614+
auto diag = diagnose(Tok, diag::attr_expected_colon_after_label,
3615+
"message");
3616+
if (Tok.is(tok::string_literal))
3617+
diag.fixItInsertAfter(labelLoc, ":");
3618+
else
3619+
return makeParserError();
36153620
}
3616-
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
3617-
.fixItReplace(Tok.getLoc(), ": ");
3618-
consumeToken();
3621+
// If the argument list just abruptly cuts off, handle that as a
3622+
// missing argument (below). Otherwise, diagnose the missing label.
3623+
else if (tokMayBeArgument()) {
3624+
if (labelLoc.isValid())
3625+
// The user wrote an explicitly omitted label (`_:`).
3626+
diagnose(labelLoc, diag::attr_expected_label, "message", AttrName)
3627+
.fixItReplace(labelLoc, "message");
3628+
else
3629+
diagnose(Tok, diag::attr_expected_label, "message", AttrName)
3630+
.fixItInsert(Tok.getLoc(), "message: ");
3631+
}
3632+
// Fall through to parse the argument.
3633+
} else if (label != Context.Id_message) {
3634+
diagnose(labelLoc, diag::attr_unknown_option, label.str(), AttrName)
3635+
.fixItReplace(labelLoc, "message");
3636+
return makeParserError();
36193637
}
3638+
36203639
if (!Tok.is(tok::string_literal)) {
3621-
diagnose(Tok.getLoc(), diag::attr_expected_string_literal, AttrName);
3622-
return makeParserSuccess();
3640+
// If this token looks like an argument, replace it; otherwise insert
3641+
// before it.
3642+
auto endLoc = tokMayBeArgument() ? peekToken().getLoc() : Tok.getLoc();
3643+
3644+
diagnose(Tok, diag::attr_expected_string_literal, AttrName)
3645+
.fixItReplaceChars(Tok.getLoc(), endLoc, "\"<#error message#>\"");
3646+
3647+
return makeParserError();
36233648
}
36243649

36253650
std::optional<StringRef> value =
3626-
getStringLiteralIfNotInterpolated(Tok.getLoc(), flag);
3651+
getStringLiteralIfNotInterpolated(Tok.getLoc(), "message");
36273652
if (!value)
3628-
return makeParserSuccess();
3653+
return makeParserError();
36293654
Token stringTok = Tok;
36303655
consumeToken();
36313656
message = *value;

lib/Parse/ParseExpr.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,9 +2148,14 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
21482148
AppendingExpr));
21492149
}
21502150

2151-
void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) {
2151+
void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc,
2152+
bool isAttr) {
2153+
/// A token that has the same meaning as colon, but is deprecated, if one exists for this call.
2154+
auto altColon = isAttr ? tok::equal : tok::NUM_TOKENS;
2155+
21522156
// Check to see if there is an argument label.
2153-
if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
2157+
if (Tok.canBeArgumentLabel() && peekToken().isAny(tok::colon, altColon)) {
2158+
// Label found, including colon.
21542159
auto text = Tok.getText();
21552160

21562161
// If this was an escaped identifier that need not have been escaped, say
@@ -2168,11 +2173,23 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) {
21682173
}
21692174

21702175
loc = consumeArgumentLabel(name, /*diagnoseDollarPrefix=*/false);
2171-
consumeToken(tok::colon);
2172-
} else if (Tok.is(tok::colon)) {
2173-
diagnose(Tok, diag::expected_label_before_colon);
2174-
consumeToken(tok::colon);
2176+
} else if (Tok.isAny(tok::colon, altColon)) {
2177+
// Found only the colon.
2178+
diagnose(Tok, diag::expected_label_before_colon)
2179+
.fixItInsert(Tok.getLoc(), "<#label#>");
2180+
} else {
2181+
// No label here.
2182+
return;
21752183
}
2184+
2185+
// If we get here, we ought to be on the colon.
2186+
assert(Tok.isAny(tok::colon, altColon));
2187+
2188+
if (Tok.is(altColon))
2189+
diagnose(Tok, diag::replace_equal_with_colon_for_value)
2190+
.fixItReplace(Tok.getLoc(), ": ");
2191+
2192+
consumeToken();
21762193
}
21772194

21782195
static bool tryParseArgLabelList(Parser &P, Parser::DeclNameOptions flags,

test/Concurrency/unavailable_from_async.swift

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,25 +140,54 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF
140140

141141
// Parsing tests
142142

143+
// expected-error@+1 {{expected label 'message:' in '@_unavailableFromAsync' attribute}} {{24-24=message: }}
144+
@_unavailableFromAsync("almost right, but not quite")
145+
func blarp0() {}
146+
147+
// expected-error@+1 {{expected label 'message:' in '@_unavailableFromAsync' attribute}} {{24-25=message}}
148+
@_unavailableFromAsync(_: "almost right, but not quite")
149+
func blarp0a() {}
150+
143151
// expected-error@+2 {{expected declaration}}
144-
// expected-error@+1:24{{unknown option 'nope' for attribute '_unavailableFromAsync'}}
152+
// expected-error@+1:24{{unknown option 'nope' for attribute '_unavailableFromAsync'}} {{24-28=message}}
145153
@_unavailableFromAsync(nope: "almost right, but not quite")
146154
func blarp1() {}
147155

148156
// expected-error@+2 {{expected declaration}}
149-
// expected-error@+1 {{expected ':' after label 'message'}}
157+
// expected-error@+1 {{expected ':' after label 'message'}} {{none}}
150158
@_unavailableFromAsync(message; "almost right, but not quite")
151159
func blarp2() {}
152160

161+
// expected-error@+1 {{expected ':' after label 'message'}} {{31-31=:}}
162+
@_unavailableFromAsync(message "almost right, but not quite")
163+
func blarp2a() {}
164+
153165
// expected-error@+1:31 {{'=' has been replaced with ':' in attribute arguments}}{{31-32=: }}
154166
@_unavailableFromAsync(message="almost right, but not quite")
155167
func blarp3() {}
156168

157169
// expected-error@+2 {{expected declaration}}
158-
// expected-error@+1 {{expected string literal in '_unavailableFromAsync' attribute}}
170+
// expected-error@+1 {{expected string literal in '_unavailableFromAsync' attribute}} {{33-35="<#error message#>"}}
159171
@_unavailableFromAsync(message: 32)
160172
func blarp4() {}
161173

174+
// expected-error@+2 {{expected declaration}}
175+
// expected-error@+1:24{{unknown option 'fnord' for attribute '_unavailableFromAsync'}} {{24-29=message}}
176+
@_unavailableFromAsync(fnord: 32)
177+
func blarp4a() {}
178+
179+
// expected-error@+3 {{expected declaration}}
180+
// expected-error@+2 {{expected label 'message:' in '@_unavailableFromAsync' attribute}} {{24-25=message}}
181+
// expected-error@+1 {{expected string literal in '_unavailableFromAsync' attribute}} {{27-29="<#error message#>"}}
182+
@_unavailableFromAsync(_: 32)
183+
func blarp4b() {}
184+
185+
// expected-error@+3 {{expected declaration}}
186+
// expected-error@+2 {{expected string literal in '_unavailableFromAsync' attribute}} {{24-26="<#error message#>"}}
187+
// expected-error@+1 {{expected label 'message:' in '@_unavailableFromAsync' attribute}} {{24-24=message: }}
188+
@_unavailableFromAsync(32)
189+
func blarp4c() {}
190+
162191
// expected-error@+2 {{expected declaration}}
163192
// expected-error@+1 {{message cannot be an interpolated string}}
164193
@_unavailableFromAsync(message: "blarppy blarp \(31 + 10)")

0 commit comments

Comments
 (0)