@@ -3780,35 +3780,60 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
3780
3780
case DeclAttrKind::UnavailableFromAsync: {
3781
3781
StringRef message;
3782
3782
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
+ };
3788
3787
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 ();
3798
3803
}
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 ();
3802
3820
}
3821
+
3803
3822
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 ();
3806
3831
}
3807
3832
3808
3833
std::optional<StringRef> value =
3809
- getStringLiteralIfNotInterpolated (Tok.getLoc (), flag );
3834
+ getStringLiteralIfNotInterpolated (Tok.getLoc (), " message " );
3810
3835
if (!value)
3811
- return makeParserSuccess ();
3836
+ return makeParserError ();
3812
3837
Token stringTok = Tok;
3813
3838
consumeToken ();
3814
3839
message = *value;
0 commit comments