Skip to content

Commit 3cd3c04

Browse files
committed
[Diagnostics] Add fix-its for missing set and ) of access modifier
1 parent 9df52be commit 3cd3c04

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

include/swift/Parse/Parser.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,13 @@ class Parser {
690690
return Context.LangOpts.EnableExperimentalConcurrency;
691691
}
692692

693+
/// Returns true if a a Swift declaration starts after the current token, otherwise returns false.
694+
bool isNextStartOfSwiftDecl() {
695+
BacktrackingScope backtrack(*this);
696+
consumeToken();
697+
return isStartOfSwiftDecl();
698+
}
699+
693700
public:
694701
InFlightDiagnostic diagnose(SourceLoc Loc, DiagRef Diag) {
695702
if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) &&

lib/Parse/ParseDecl.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)