[CodeCompletion][Sema] Add fix to treat empty or single-element array literals as dictionaries when used as such

In the single-element case, it is treated as the dictionary key.

func takesDict(_ x: [Int: String]) {}
takesDict([]) // diagnose with fixit to add missing ':'
takesDict([1]) // diagnose with fixit to add missing ': <#value#>'
takesDict([foo.<complete>]) // prioritise Int members in completion results -
                            // the user just hasn't written the value yet.

The above previously failed with a generic mismatch error in normal type
checking (due to the literal being parsed as an array literal) and code
completion could not pick up the expected type from the context.
This commit is contained in:
Nathan Hawes
2020-10-30 12:11:53 -07:00
parent cdfff19832
commit edbbefce91
8 changed files with 307 additions and 33 deletions

View File

@@ -841,6 +841,27 @@ bool LabelingFailure::diagnoseAsNote() {
return false;
}
bool ArrayLiteralToDictionaryConversionFailure::diagnoseAsError() {
ArrayExpr *AE = getAsExpr<ArrayExpr>(getAnchor());
assert(AE);
if (AE->getNumElements() == 0) {
emitDiagnostic(diag::should_use_empty_dictionary_literal)
.fixItInsertAfter(getLoc(), ":");
return true;
}
auto CTP = getConstraintSystem().getContextualTypePurpose(AE);
emitDiagnostic(diag::should_use_dictionary_literal,
getToType()->lookThroughAllOptionalTypes(),
CTP == CTP_Initialization);
auto diagnostic = emitDiagnostic(diag::meant_dictionary_lit);
if (AE->getNumElements() == 1)
diagnostic.fixItInsertAfter(AE->getElement(0)->getEndLoc(), ": <#value#>");
return true;
}
bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
if (diagnoseParameterUse())
return true;
@@ -5129,16 +5150,27 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
auto eltType = getFromType();
auto contextualType = getToType();
Optional<InFlightDiagnostic> diagnostic;
if (isExpr<ArrayExpr>(anchor)) {
if (diagnoseMergedLiteralElements())
return true;
auto isFixedToDictionary = [&](ArrayExpr *anchor) {
return llvm::any_of(getSolution().Fixes, [&](ConstraintFix *fix) {
auto *fixAnchor = getAsExpr<ArrayExpr>(fix->getAnchor());
return fixAnchor && fixAnchor == anchor &&
fix->getKind() == FixKind::TreatArrayLiteralAsDictionary;
});
};
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element,
eltType, contextualType));
bool treatAsDictionary = false;
Optional<InFlightDiagnostic> diagnostic;
if (auto *AE = getAsExpr<ArrayExpr>(anchor)) {
if (!(treatAsDictionary = isFixedToDictionary(AE))) {
if (diagnoseMergedLiteralElements())
return true;
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element,
eltType, contextualType));
}
}
if (isExpr<DictionaryExpr>(anchor)) {
if (treatAsDictionary || isExpr<DictionaryExpr>(anchor)) {
auto eltLoc = locator->castLastElementTo<LocatorPathElt::TupleElement>();
switch (eltLoc.getIndex()) {
case 0: // key