Skip to content

Commit e44e0d9

Browse files
authored
[Diag] Make fixItReplace slightly smart
Merge pull request #4499 from rintaro/fixitreplace-smart
2 parents 0c11ba8 + 96600fe commit e44e0d9

File tree

6 files changed

+57
-43
lines changed

6 files changed

+57
-43
lines changed

lib/AST/DiagnosticEngine.cpp

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@ static CharSourceRange toCharSourceRange(SourceManager &SM, SourceLoc Start,
105105
return CharSourceRange(SM, Start, End);
106106
}
107107

108+
/// \brief Extract a character at \p Loc. If \p Loc is the end of the buffer,
109+
/// return '\f'.
110+
static char extractCharAfter(SourceManager &SM, SourceLoc Loc) {
111+
auto chars = SM.extractText({Loc, 1});
112+
return chars.empty() ? '\f' : chars[0];
113+
}
114+
115+
/// \brief Extract a character immediately before \p Loc. If \p Loc is the
116+
/// start of the buffer, return '\f'.
117+
static char extractCharBefore(SourceManager &SM, SourceLoc Loc) {
118+
// We have to be careful not to go off the front of the buffer.
119+
auto bufferID = SM.findBufferContainingLoc(Loc);
120+
auto bufferRange = SM.getRangeForBuffer(bufferID);
121+
if (bufferRange.getStart() == Loc)
122+
return '\f';
123+
auto chars = SM.extractText({Loc.getAdvancedLoc(-1), 1}, bufferID);
124+
assert(!chars.empty() && "Couldn't extractText with valid range");
125+
return chars[0];
126+
}
127+
108128
InFlightDiagnostic &InFlightDiagnostic::highlight(SourceRange R) {
109129
assert(IsActive && "Cannot modify an inactive diagnostic");
110130
if (Engine && R.isValid())
@@ -147,23 +167,10 @@ InFlightDiagnostic &InFlightDiagnostic::fixItRemove(SourceRange R) {
147167
// space around its hole. Specifically, check to see there is whitespace
148168
// before and after the end of range. If so, nuke the space afterward to keep
149169
// things consistent.
150-
if (SM.extractText({charRange.getEnd(), 1}) == " ") {
151-
// Check before the string, we have to be careful not to go off the front of
152-
// the buffer.
153-
auto bufferRange =
154-
SM.getRangeForBuffer(SM.findBufferContainingLoc(charRange.getStart()));
155-
bool ShouldRemove = false;
156-
if (bufferRange.getStart() == charRange.getStart())
157-
ShouldRemove = true;
158-
else {
159-
auto beforeChars =
160-
SM.extractText({charRange.getStart().getAdvancedLoc(-1), 1});
161-
ShouldRemove = !beforeChars.empty() && isspace(beforeChars[0]);
162-
}
163-
if (ShouldRemove) {
164-
charRange = CharSourceRange(charRange.getStart(),
165-
charRange.getByteLength()+1);
166-
}
170+
if (extractCharAfter(SM, charRange.getEnd()) == ' ' &&
171+
isspace(extractCharBefore(SM, charRange.getStart()))) {
172+
charRange = CharSourceRange(charRange.getStart(),
173+
charRange.getByteLength()+1);
167174
}
168175
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, {}));
169176
return *this;
@@ -176,9 +183,23 @@ InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
176183
return fixItRemove(R);
177184

178185
assert(IsActive && "Cannot modify an inactive diagnostic");
179-
if (Engine && R.isValid())
180-
Engine->getActiveDiagnostic().addFixIt(
181-
Diagnostic::FixIt(toCharSourceRange(Engine->SourceMgr, R), Str));
186+
if (R.isInvalid() || !Engine) return *this;
187+
188+
auto &SM = Engine->SourceMgr;
189+
auto charRange = toCharSourceRange(SM, R);
190+
191+
// If we're replacing with something that wants spaces around it, do a bit of
192+
// extra work so that we don't suggest extra spaces.
193+
if (Str.back() == ' ') {
194+
if (isspace(extractCharAfter(SM, charRange.getEnd())))
195+
Str = Str.drop_back();
196+
}
197+
if (!Str.empty() && Str.front() == ' ') {
198+
if (isspace(extractCharBefore(SM, charRange.getStart())))
199+
Str = Str.drop_front();
200+
}
201+
202+
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, Str));
182203
return *this;
183204
}
184205

lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
333333
if (!Tok.is(tok::equal))
334334
return false;
335335

336-
bool wantSpace = (Tok.getRange().getEnd() == peekToken().getLoc());
337336
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
338-
.fixItReplace(Tok.getLoc(), wantSpace ? ": " : ":");
337+
.fixItReplace(Tok.getLoc(), ": ");
339338
}
340339
return true;
341340
};
@@ -1410,11 +1409,8 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
14101409
if (Attributes.has(TAK_noescape)) {
14111410
diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure);
14121411
} else {
1413-
StringRef replacement = " @escaping ";
1414-
if (autoclosureEscapingParenRange.End.getAdvancedLoc(1) != Tok.getLoc())
1415-
replacement = replacement.drop_back();
14161412
diagnose(Loc, diag::attr_autoclosure_escaping_deprecated)
1417-
.fixItReplace(autoclosureEscapingParenRange, replacement);
1413+
.fixItReplace(autoclosureEscapingParenRange, " @escaping ");
14181414
}
14191415
Attributes.setAttr(TAK_escaping, Loc);
14201416
} else if (Attributes.has(TAK_noescape)) {
@@ -2839,7 +2835,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) {
28392835
// It is a common mistake to write "typealias A : Int" instead of = Int.
28402836
// Recognize this and produce a fixit.
28412837
diagnose(Tok, diag::expected_equal_in_typealias)
2842-
.fixItReplace(Tok.getLoc(), "=");
2838+
.fixItReplace(Tok.getLoc(), " = ");
28432839
consumeToken(tok::colon);
28442840
} else {
28452841
consumeToken(tok::equal);

lib/Parse/ParsePattern.cpp

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ Parser::parseFunctionSignature(Identifier SimpleName,
696696
if (!consumeIf(tok::arrow, arrowLoc)) {
697697
// FixIt ':' to '->'.
698698
diagnose(Tok, diag::func_decl_expected_arrow)
699-
.fixItReplace(SourceRange(Tok.getLoc()), "->");
699+
.fixItReplace(Tok.getLoc(), " -> ");
700700
arrowLoc = consumeToken(tok::colon);
701701
}
702702

@@ -787,12 +787,10 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
787787

788788
// Now parse an optional type annotation.
789789
if (Tok.is(tok::colon)) {
790-
SourceLoc pastEndOfPrevLoc = getEndOfPreviousLoc();
791790
SourceLoc colonLoc = consumeToken(tok::colon);
792-
SourceLoc startOfNextLoc = Tok.getLoc();
793791

794792
if (result.isNull()) // Recover by creating AnyPattern.
795-
result = makeParserErrorResult(new (Context) AnyPattern(PreviousLoc));
793+
result = makeParserErrorResult(new (Context) AnyPattern(colonLoc));
796794

797795
ParserResult<TypeRepr> Ty = parseType();
798796
if (Ty.hasCodeCompletion())
@@ -824,19 +822,10 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
824822
if (status.isSuccess()) {
825823
backtrack.cancelBacktrack();
826824

827-
// Suggest replacing ':' with '=' (ensuring proper whitespace).
828-
829-
bool needSpaceBefore = (pastEndOfPrevLoc == colonLoc);
830-
bool needSpaceAfter =
831-
SourceMgr.getByteDistance(colonLoc, startOfNextLoc) <= 1;
832-
833-
StringRef replacement = " = ";
834-
if (!needSpaceBefore) replacement = replacement.drop_front();
835-
if (!needSpaceAfter) replacement = replacement.drop_back();
836-
825+
// Suggest replacing ':' with '='
837826
diagnose(lParenLoc, diag::initializer_as_typed_pattern)
838827
.highlight({Ty.get()->getStartLoc(), rParenLoc})
839-
.fixItReplace(colonLoc, replacement);
828+
.fixItReplace(colonLoc, " = ");
840829
result.setIsParseError();
841830
}
842831
}

test/Parse/diagnose_initializer_as_typed_pattern.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ let d : [X]() // expected-error{{unexpected initializer in pattern; did you mea
1212

1313
let e: X(), ee: Int // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
1414

15+
let f:/*comment*/[X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= = }}
16+
1517
var _1 = 1, _2 = 2
1618

1719
// paren follows the type, but it's part of a separate (valid) expression

test/Parse/typealias.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ var fiveInts : FiveInts = ((4,2), (1,2,3))
99

1010

1111
// <rdar://problem/13339798> QoI: poor diagnostic in malformed typealias
12-
typealias Foo : Int // expected-error {{expected '=' in typealias declaration}} {{15-16==}}
12+
typealias Foo1 : Int // expected-error {{expected '=' in typealias declaration}} {{16-17==}}
13+
typealias Foo2: Int // expected-error {{expected '=' in typealias declaration}} {{15-16= =}}
14+
typealias Foo3 :Int // expected-error {{expected '=' in typealias declaration}} {{16-17== }}
15+
typealias Foo4:/*comment*/Int // expected-error {{expected '=' in typealias declaration}} {{15-16= = }}
1316

1417
//===--- Tests for error recovery.
1518

test/decl/func/functions.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ func recover_colon_arrow_1() : Int { } // expected-error {{expected '->' after f
4949
func recover_colon_arrow_2() : { } // expected-error {{expected '->' after function parameter tuple}} {{30-31=->}} expected-error {{expected type for function result}}
5050
func recover_colon_arrow_3 : Int { } // expected-error {{expected '->' after function parameter tuple}} {{28-29=->}} expected-error {{expected '(' in argument list of function declaration}}
5151
func recover_colon_arrow_4 : { } // expected-error {{expected '->' after function parameter tuple}} {{28-29=->}} expected-error {{expected '(' in argument list of function declaration}} expected-error {{expected type for function result}}
52+
func recover_colon_arrow_5():Int { } // expected-error {{expected '->' after function parameter tuple}} {{29-30= -> }}
53+
func recover_colon_arrow_6(): Int { } // expected-error {{expected '->' after function parameter tuple}} {{29-30= ->}}
54+
func recover_colon_arrow_7() :Int { } // expected-error {{expected '->' after function parameter tuple}} {{30-31=-> }}
5255

5356
//===--- Check that we recover if the function does not have a body, but the
5457
//===--- context requires the function to have a body.

0 commit comments

Comments
 (0)