@@ -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;
0 commit comments