[CSDiagnostics] Diagnose contextual closure result mismatches via fixes

Let's keep track of type mismatch between type deduced
for the body of the closure vs. what is requested
contextually, it makes it much easier to diagnose
problems like:

```swift
func foo(_: () -> Int) {}
foo { "hello" }
```

Because we can pin-point problematic area of the source
when the rest of the system is consistent.

Resolves: rdar://problem/40537960
This commit is contained in:
Pavel Yaskevich
2018-11-07 14:23:11 -08:00
parent 6e938f8359
commit 66a79301b4
11 changed files with 241 additions and 79 deletions

View File

@@ -1106,3 +1106,90 @@ Diag<StringRef> AssignmentFailure::findDeclDiagonstic(ASTContext &ctx,
return diag::assignment_lhs_is_immutable_variable;
}
bool ContextualFailure::diagnoseAsError() {
auto *anchor = getAnchor();
auto path = getLocator()->getPath();
assert(!path.empty());
if (diagnoseMissingFunctionCall())
return true;
Diag<Type, Type> diagnostic;
switch (path.back().getKind()) {
case ConstraintLocator::ClosureResult: {
diagnostic = diag::cannot_convert_closure_result;
break;
}
default:
return false;
}
auto diag = emitDiagnostic(anchor->getLoc(), diagnostic, FromType, ToType);
diag.highlight(anchor->getSourceRange());
(void)trySequenceSubsequenceFixIts(diag, getConstraintSystem(), FromType,
ToType, anchor);
return true;
}
bool ContextualFailure::diagnoseMissingFunctionCall() const {
auto &TC = getTypeChecker();
auto *srcFT = FromType->getAs<FunctionType>();
if (!srcFT || !srcFT->getParams().empty())
return false;
if (ToType->is<AnyFunctionType>() ||
!TC.isConvertibleTo(srcFT->getResult(), ToType, getDC()))
return false;
auto *anchor = getAnchor();
emitDiagnostic(anchor->getLoc(), diag::missing_nullary_call,
srcFT->getResult())
.highlight(anchor->getSourceRange())
.fixItInsertAfter(anchor->getEndLoc(), "()");
return true;
}
bool ContextualFailure::trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
ConstraintSystem &CS,
Type fromType, Type toType,
Expr *expr) {
if (!CS.TC.Context.getStdlibModule())
return false;
auto String = CS.TC.getStringType(CS.DC);
auto Substring = CS.TC.getSubstringType(CS.DC);
if (!String || !Substring)
return false;
/// FIXME: Remove this flag when void subscripts are implemented.
/// Make this unconditional and remove the if statement.
if (CS.TC.getLangOpts().FixStringToSubstringConversions) {
// String -> Substring conversion
// Add '[]' void subscript call to turn the whole String into a Substring
if (fromType->isEqual(String)) {
if (toType->isEqual(Substring)) {
diag.fixItInsertAfter(expr->getEndLoc(), "[]");
return true;
}
}
}
// Substring -> String conversion
// Wrap in String.init
if (fromType->isEqual(Substring)) {
if (toType->isEqual(String)) {
auto range = expr->getSourceRange();
diag.fixItInsert(range.Start, "String(");
diag.fixItInsertAfter(range.End, ")");
return true;
}
}
return false;
}