@@ -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,43 @@ 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);
2927-
2923+ if (!Tok.isContextualKeyword (" set" )) {
2924+ auto diag = diagnose (Loc, diag::attr_access_expected_set, AttrName);
2925+
29282926 // Minimal recovery: if there's a single token and then an r_paren,
29292927 // 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- }
2928+ if (Tok.is (tok::r_paren)) {
2929+ // Suggest `set` between empty parens e.g. `private()` -> `private(set)`
2930+ auto SetLoc = consumeToken (tok::r_paren);
2931+ diag.fixItInsert (SetLoc, " set" );
2932+ } else if (!Tok.is (tok::l_paren) && peekToken ().is (tok::r_paren)) {
2933+ // Suggest `set` in place of an invalid token between parens
2934+ // e.g. `private(<invalid>)` -> `private(set)`
2935+ auto SetLoc = consumeToken ();
2936+ diag.fixItReplace (SetLoc, " set" );
2937+ consumeToken (tok::r_paren);
2938+ } else if (isNextStartOfSwiftDecl ()) {
2939+ // Suggest `set)` in place of an invalid token after l_paren followed by
2940+ // a valid declaration start.
2941+ // e.g. `private( var x: Int` -> `private(set) var x: Int`
2942+ diag.fixItReplace (Tok.getLoc (), " set)" );
2943+ } else {
2944+ // Suggest `set)` after l_paren if not followed by a valid declaration
2945+ // e.g. `private( val x: Int` -> `private(set) val x: Int`
2946+ diag.fixItInsertAfter (LParenLoc, " set)" );
29352947 }
2948+
29362949 return makeParserSuccess ();
29372950 }
2951+
2952+ auto SubjectLoc = consumeToken ();
29382953
29392954 AttrRange = SourceRange (Loc, Tok.getLoc ());
29402955
29412956 if (!consumeIf (tok::r_paren)) {
29422957 diagnose (Loc, diag::attr_expected_rparen, AttrName,
2943- DeclAttribute::isDeclModifier (DK));
2958+ DeclAttribute::isDeclModifier (DK))
2959+ .fixItInsertAfter (SubjectLoc, " )" );
29442960 return makeParserSuccess ();
29452961 }
29462962
0 commit comments