@@ -2908,7 +2908,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
29082908 break ;
29092909 }
29102910
2911- consumeAttributeLParen ();
2911+ auto LParenLoc = consumeAttributeLParen ();
29122912
29132913 if (Tok.is (tok::code_complete)) {
29142914 if (CodeCompletionCallbacks) {
@@ -2920,27 +2920,42 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
29202920 }
29212921
29222922 // Parse the subject.
2923- if (Tok.isContextualKeyword (" set" )) {
2924- consumeToken ();
2925- } else {
2926- diagnose (Loc, diag::attr_access_expected_set, AttrName);
2923+ if (!Tok.isContextualKeyword (" set" )) {
2924+ auto diag = diagnose (Loc, diag::attr_access_expected_set, AttrName);
29272925
2928- // Minimal recovery: if there's a single token and then an r_paren,
2929- // consume them both. If there's just an r_paren, consume that.
2930- if (!consumeIf (tok::r_paren)) {
2931- if (Tok.isNot (tok::l_paren) && peekToken ().is (tok::r_paren)) {
2932- consumeToken ();
2933- consumeToken (tok::r_paren);
2934- }
2926+ if (Tok.is (tok::r_paren)) {
2927+ // Suggest `set` between empty parens e.g. `private()` -> `private(set)`
2928+ auto SetLoc = consumeToken (tok::r_paren);
2929+ diag.fixItInsert (SetLoc, " set" );
2930+ } else if (!Tok.is (tok::l_paren) && peekToken ().is (tok::r_paren)) {
2931+ // Suggest `set` in place of an invalid token between parens
2932+ // e.g. `private(<invalid>)` -> `private(set)`
2933+ auto SetLoc = consumeToken ();
2934+ diag.fixItReplace (SetLoc, " set" );
2935+ consumeToken (tok::r_paren);
2936+ } else if (isNextStartOfSwiftDecl ()) {
2937+ // Suggest `set)` in place of an invalid token after l_paren followed by
2938+ // a valid declaration start.
2939+ // e.g. `private(<invalid> var x: Int` -> `private(set) var x: Int`
2940+ auto SetLoc = consumeToken ();
2941+ diag.fixItReplace (SetLoc, " set)" );
2942+ } else {
2943+ // Suggest `set)` after l_paren if not followed by a valid declaration
2944+ // e.g. `private( <invalid>` -> `private(set) <invalid>`
2945+ diag.fixItInsertAfter (LParenLoc, " set)" );
29352946 }
2947+
29362948 return makeParserSuccess ();
29372949 }
29382950
2951+ auto SubjectLoc = consumeToken ();
2952+
29392953 AttrRange = SourceRange (Loc, Tok.getLoc ());
29402954
29412955 if (!consumeIf (tok::r_paren)) {
29422956 diagnose (Loc, diag::attr_expected_rparen, AttrName,
2943- DeclAttribute::isDeclModifier (DK));
2957+ DeclAttribute::isDeclModifier (DK))
2958+ .fixItInsertAfter (SubjectLoc, " )" );
29442959 return makeParserSuccess ();
29452960 }
29462961
0 commit comments