@@ -3780,35 +3780,60 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
37803780 case DeclAttrKind::UnavailableFromAsync: {
37813781 StringRef message;
37823782 if (consumeIfAttributeLParen ()) {
3783- if (!Tok.is (tok::identifier)) {
3784- llvm_unreachable (" Flag must start with an identifier" );
3785- }
3786-
3787- StringRef flag = Tok.getText ();
3783+ auto tokMayBeArgument = [&]() -> bool {
3784+ return Tok.isNot (tok::r_paren, tok::comma) &&
3785+ !isKeywordPossibleDeclStart (Context.LangOpts , Tok);
3786+ };
37883787
3789- if (flag != " message" ) {
3790- diagnose (Tok.getLoc (), diag::attr_unknown_option, flag, AttrName);
3791- return makeParserError ();
3792- }
3793- consumeToken ();
3794- if (!consumeIf (tok::colon)) {
3795- if (!Tok.is (tok::equal)) {
3796- diagnose (Tok.getLoc (), diag::attr_expected_colon_after_label, flag);
3797- return makeParserSuccess ();
3788+ Identifier label;
3789+ SourceLoc labelLoc;
3790+ parseOptionalArgumentLabel (label, labelLoc, /* isAttr=*/ true );
3791+
3792+ if (label.empty ()) {
3793+ // If we have the identifier 'message', assume the user forgot the
3794+ // colon.
3795+ if (Tok.isContextualKeyword (" message" )) {
3796+ labelLoc = consumeToken ();
3797+ auto diag = diagnose (Tok, diag::attr_expected_colon_after_label,
3798+ " message" );
3799+ if (Tok.is (tok::string_literal))
3800+ diag.fixItInsertAfter (labelLoc, " :" );
3801+ else
3802+ return makeParserError ();
37983803 }
3799- diagnose (Tok.getLoc (), diag::replace_equal_with_colon_for_value)
3800- .fixItReplace (Tok.getLoc (), " : " );
3801- consumeToken ();
3804+ // If the argument list just abruptly cuts off, handle that as a
3805+ // missing argument (below). Otherwise, diagnose the missing label.
3806+ else if (tokMayBeArgument ()) {
3807+ if (labelLoc.isValid ())
3808+ // The user wrote an explicitly omitted label (`_:`).
3809+ diagnose (labelLoc, diag::attr_expected_label, " message" , AttrName)
3810+ .fixItReplace (labelLoc, " message" );
3811+ else
3812+ diagnose (Tok, diag::attr_expected_label, " message" , AttrName)
3813+ .fixItInsert (Tok.getLoc (), " message: " );
3814+ }
3815+ // Fall through to parse the argument.
3816+ } else if (label != Context.Id_message ) {
3817+ diagnose (labelLoc, diag::attr_unknown_option, label.str (), AttrName)
3818+ .fixItReplace (labelLoc, " message" );
3819+ return makeParserError ();
38023820 }
3821+
38033822 if (!Tok.is (tok::string_literal)) {
3804- diagnose (Tok.getLoc (), diag::attr_expected_string_literal, AttrName);
3805- return makeParserSuccess ();
3823+ // If this token looks like an argument, replace it; otherwise insert
3824+ // before it.
3825+ auto endLoc = tokMayBeArgument () ? peekToken ().getLoc () : Tok.getLoc ();
3826+
3827+ diagnose (Tok, diag::attr_expected_string_literal, AttrName)
3828+ .fixItReplaceChars (Tok.getLoc (), endLoc, " \" <#error message#>\" " );
3829+
3830+ return makeParserError ();
38063831 }
38073832
38083833 std::optional<StringRef> value =
3809- getStringLiteralIfNotInterpolated (Tok.getLoc (), flag );
3834+ getStringLiteralIfNotInterpolated (Tok.getLoc (), " message " );
38103835 if (!value)
3811- return makeParserSuccess ();
3836+ return makeParserError ();
38123837 Token stringTok = Tok;
38133838 consumeToken ();
38143839 message = *value;
0 commit comments