Skip to content

Commit ba982bc

Browse files
committed
[Diag] Make fixItReplace slightly smart
When replace something with a punctuator, we often prefer adding spaces around it. For instance, func foo(): bar {} // fix it func foo() -> bar {} In this case we want to add a space before '->', but not after that. With this change, we can simply `fixItReplace(ColonLoc, " -> ")`. `fixItReplace()` automatically adjust the spaces around it.
1 parent 35f3f71 commit ba982bc

File tree

6 files changed

+45
-26
lines changed

6 files changed

+45
-26
lines changed

lib/AST/DiagnosticEngine.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,35 @@ InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
176176
return fixItRemove(R);
177177

178178
assert(IsActive && "Cannot modify an inactive diagnostic");
179-
if (Engine && R.isValid())
180-
Engine->getActiveDiagnostic().addFixIt(
181-
Diagnostic::FixIt(toCharSourceRange(Engine->SourceMgr, R), Str));
179+
if (R.isInvalid() || !Engine) return *this;
180+
181+
auto &SM = Engine->SourceMgr;
182+
auto charRange = toCharSourceRange(Engine->SourceMgr, R);
183+
184+
// If we're replacing with something that wants spaces around it, do a bit of
185+
// extra work so that we don't suggest extra spaces.
186+
if (Str.back() == ' ') {
187+
auto afterChars = SM.extractText({charRange.getEnd(), 1});
188+
if (!afterChars.empty() && isspace(afterChars[0])) {
189+
Str = Str.drop_back();
190+
}
191+
}
192+
if (!Str.empty() && Str.front() == ' ') {
193+
bool ShouldRemove = false;
194+
auto buffer = SM.findBufferContainingLoc(charRange.getStart());
195+
if (SM.getLocForBufferStart(buffer) == charRange.getStart()) {
196+
ShouldRemove = true;
197+
} else {
198+
auto beforeChars =
199+
SM.extractText({charRange.getStart().getAdvancedLoc(-1), 1});
200+
ShouldRemove = !beforeChars.empty() && isspace(beforeChars[0]);
201+
}
202+
if (ShouldRemove) {
203+
Str = Str.drop_front();
204+
}
205+
}
206+
207+
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, Str));
182208
return *this;
183209
}
184210

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)) {
@@ -2837,7 +2833,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) {
28372833
// It is a common mistake to write "typealias A : Int" instead of = Int.
28382834
// Recognize this and produce a fixit.
28392835
diagnose(Tok, diag::expected_equal_in_typealias)
2840-
.fixItReplace(Tok.getLoc(), "=");
2836+
.fixItReplace(Tok.getLoc(), " = ");
28412837
consumeToken(tok::colon);
28422838
} else {
28432839
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)