[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.
This commit is contained in:
Rintaro Ishizaki
2016-07-21 01:14:01 +09:00
parent 35f3f7169b
commit ba982bc01c
6 changed files with 45 additions and 26 deletions

View File

@@ -176,9 +176,35 @@ InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
return fixItRemove(R); return fixItRemove(R);
assert(IsActive && "Cannot modify an inactive diagnostic"); assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine && R.isValid()) if (R.isInvalid() || !Engine) return *this;
Engine->getActiveDiagnostic().addFixIt(
Diagnostic::FixIt(toCharSourceRange(Engine->SourceMgr, R), Str)); auto &SM = Engine->SourceMgr;
auto charRange = toCharSourceRange(Engine->SourceMgr, R);
// If we're replacing with something that wants spaces around it, do a bit of
// extra work so that we don't suggest extra spaces.
if (Str.back() == ' ') {
auto afterChars = SM.extractText({charRange.getEnd(), 1});
if (!afterChars.empty() && isspace(afterChars[0])) {
Str = Str.drop_back();
}
}
if (!Str.empty() && Str.front() == ' ') {
bool ShouldRemove = false;
auto buffer = SM.findBufferContainingLoc(charRange.getStart());
if (SM.getLocForBufferStart(buffer) == charRange.getStart()) {
ShouldRemove = true;
} else {
auto beforeChars =
SM.extractText({charRange.getStart().getAdvancedLoc(-1), 1});
ShouldRemove = !beforeChars.empty() && isspace(beforeChars[0]);
}
if (ShouldRemove) {
Str = Str.drop_front();
}
}
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, Str));
return *this; return *this;
} }

View File

@@ -333,9 +333,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
if (!Tok.is(tok::equal)) if (!Tok.is(tok::equal))
return false; return false;
bool wantSpace = (Tok.getRange().getEnd() == peekToken().getLoc());
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value) diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
.fixItReplace(Tok.getLoc(), wantSpace ? ": " : ":"); .fixItReplace(Tok.getLoc(), ": ");
} }
return true; return true;
}; };
@@ -1410,11 +1409,8 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
if (Attributes.has(TAK_noescape)) { if (Attributes.has(TAK_noescape)) {
diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure); diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure);
} else { } else {
StringRef replacement = " @escaping ";
if (autoclosureEscapingParenRange.End.getAdvancedLoc(1) != Tok.getLoc())
replacement = replacement.drop_back();
diagnose(Loc, diag::attr_autoclosure_escaping_deprecated) diagnose(Loc, diag::attr_autoclosure_escaping_deprecated)
.fixItReplace(autoclosureEscapingParenRange, replacement); .fixItReplace(autoclosureEscapingParenRange, " @escaping ");
} }
Attributes.setAttr(TAK_escaping, Loc); Attributes.setAttr(TAK_escaping, Loc);
} else if (Attributes.has(TAK_noescape)) { } else if (Attributes.has(TAK_noescape)) {
@@ -2837,7 +2833,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) {
// It is a common mistake to write "typealias A : Int" instead of = Int. // It is a common mistake to write "typealias A : Int" instead of = Int.
// Recognize this and produce a fixit. // Recognize this and produce a fixit.
diagnose(Tok, diag::expected_equal_in_typealias) diagnose(Tok, diag::expected_equal_in_typealias)
.fixItReplace(Tok.getLoc(), "="); .fixItReplace(Tok.getLoc(), " = ");
consumeToken(tok::colon); consumeToken(tok::colon);
} else { } else {
consumeToken(tok::equal); consumeToken(tok::equal);

View File

@@ -696,7 +696,7 @@ Parser::parseFunctionSignature(Identifier SimpleName,
if (!consumeIf(tok::arrow, arrowLoc)) { if (!consumeIf(tok::arrow, arrowLoc)) {
// FixIt ':' to '->'. // FixIt ':' to '->'.
diagnose(Tok, diag::func_decl_expected_arrow) diagnose(Tok, diag::func_decl_expected_arrow)
.fixItReplace(SourceRange(Tok.getLoc()), "->"); .fixItReplace(Tok.getLoc(), " -> ");
arrowLoc = consumeToken(tok::colon); arrowLoc = consumeToken(tok::colon);
} }
@@ -787,12 +787,10 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
// Now parse an optional type annotation. // Now parse an optional type annotation.
if (Tok.is(tok::colon)) { if (Tok.is(tok::colon)) {
SourceLoc pastEndOfPrevLoc = getEndOfPreviousLoc();
SourceLoc colonLoc = consumeToken(tok::colon); SourceLoc colonLoc = consumeToken(tok::colon);
SourceLoc startOfNextLoc = Tok.getLoc();
if (result.isNull()) // Recover by creating AnyPattern. if (result.isNull()) // Recover by creating AnyPattern.
result = makeParserErrorResult(new (Context) AnyPattern(PreviousLoc)); result = makeParserErrorResult(new (Context) AnyPattern(colonLoc));
ParserResult<TypeRepr> Ty = parseType(); ParserResult<TypeRepr> Ty = parseType();
if (Ty.hasCodeCompletion()) if (Ty.hasCodeCompletion())
@@ -824,19 +822,10 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
if (status.isSuccess()) { if (status.isSuccess()) {
backtrack.cancelBacktrack(); backtrack.cancelBacktrack();
// Suggest replacing ':' with '=' (ensuring proper whitespace). // Suggest replacing ':' with '='
bool needSpaceBefore = (pastEndOfPrevLoc == colonLoc);
bool needSpaceAfter =
SourceMgr.getByteDistance(colonLoc, startOfNextLoc) <= 1;
StringRef replacement = " = ";
if (!needSpaceBefore) replacement = replacement.drop_front();
if (!needSpaceAfter) replacement = replacement.drop_back();
diagnose(lParenLoc, diag::initializer_as_typed_pattern) diagnose(lParenLoc, diag::initializer_as_typed_pattern)
.highlight({Ty.get()->getStartLoc(), rParenLoc}) .highlight({Ty.get()->getStartLoc(), rParenLoc})
.fixItReplace(colonLoc, replacement); .fixItReplace(colonLoc, " = ");
result.setIsParseError(); result.setIsParseError();
} }
} }

View File

@@ -12,6 +12,8 @@ let d : [X]() // expected-error{{unexpected initializer in pattern; did you mea
let e: X(), ee: Int // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}} let e: X(), ee: Int // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= =}}
let f:/*comment*/[X]() // expected-error{{unexpected initializer in pattern; did you mean to use '='?}} {{6-7= = }}
var _1 = 1, _2 = 2 var _1 = 1, _2 = 2
// paren follows the type, but it's part of a separate (valid) expression // paren follows the type, but it's part of a separate (valid) expression

View File

@@ -9,7 +9,10 @@ var fiveInts : FiveInts = ((4,2), (1,2,3))
// <rdar://problem/13339798> QoI: poor diagnostic in malformed typealias // <rdar://problem/13339798> QoI: poor diagnostic in malformed typealias
typealias Foo : Int // expected-error {{expected '=' in typealias declaration}} {{15-16==}} typealias Foo1 : Int // expected-error {{expected '=' in typealias declaration}} {{16-17==}}
typealias Foo2: Int // expected-error {{expected '=' in typealias declaration}} {{15-16= =}}
typealias Foo3 :Int // expected-error {{expected '=' in typealias declaration}} {{16-17== }}
typealias Foo4:/*comment*/Int // expected-error {{expected '=' in typealias declaration}} {{15-16= = }}
//===--- Tests for error recovery. //===--- Tests for error recovery.

View File

@@ -49,6 +49,9 @@ func recover_colon_arrow_1() : Int { } // expected-error {{expected '->' after f
func recover_colon_arrow_2() : { } // expected-error {{expected '->' after function parameter tuple}} {{30-31=->}} expected-error {{expected type for function result}} func recover_colon_arrow_2() : { } // expected-error {{expected '->' after function parameter tuple}} {{30-31=->}} expected-error {{expected type for function result}}
func recover_colon_arrow_3 : Int { } // expected-error {{expected '->' after function parameter tuple}} {{28-29=->}} expected-error {{expected '(' in argument list of function declaration}} func recover_colon_arrow_3 : Int { } // expected-error {{expected '->' after function parameter tuple}} {{28-29=->}} expected-error {{expected '(' in argument list of function declaration}}
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}} 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}}
func recover_colon_arrow_5():Int { } // expected-error {{expected '->' after function parameter tuple}} {{29-30= -> }}
func recover_colon_arrow_6(): Int { } // expected-error {{expected '->' after function parameter tuple}} {{29-30= ->}}
func recover_colon_arrow_7() :Int { } // expected-error {{expected '->' after function parameter tuple}} {{30-31=-> }}
//===--- Check that we recover if the function does not have a body, but the //===--- Check that we recover if the function does not have a body, but the
//===--- context requires the function to have a body. //===--- context requires the function to have a body.