mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -285,6 +285,10 @@ enum class FixKind : uint8_t {
|
|||||||
/// Allow expressions to reference invalid declarations by turning
|
/// Allow expressions to reference invalid declarations by turning
|
||||||
/// them into holes.
|
/// them into holes.
|
||||||
AllowRefToInvalidDecl,
|
AllowRefToInvalidDecl,
|
||||||
|
|
||||||
|
/// Treat empty and single-element array literals as if they were incomplete
|
||||||
|
/// dictionary literals when used as such.
|
||||||
|
TreatArrayLiteralAsDictionary,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConstraintFix {
|
class ConstraintFix {
|
||||||
@@ -551,6 +555,25 @@ public:
|
|||||||
ConstraintLocator *locator);
|
ConstraintLocator *locator);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TreatArrayLiteralAsDictionary final : public ContextualMismatch {
|
||||||
|
TreatArrayLiteralAsDictionary(ConstraintSystem &cs, Type dictionaryTy,
|
||||||
|
Type arrayTy, ConstraintLocator *locator)
|
||||||
|
: ContextualMismatch(cs, FixKind::TreatArrayLiteralAsDictionary,
|
||||||
|
dictionaryTy, arrayTy, locator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string getName() const override {
|
||||||
|
return "treat array literal as dictionary";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool diagnose(const Solution &solution, bool asNote = false) const override;
|
||||||
|
|
||||||
|
static TreatArrayLiteralAsDictionary *create(ConstraintSystem &cs,
|
||||||
|
Type dictionaryTy, Type arrayTy,
|
||||||
|
ConstraintLocator *loc);
|
||||||
|
};
|
||||||
|
|
||||||
/// Mark function type as explicitly '@escaping'.
|
/// Mark function type as explicitly '@escaping'.
|
||||||
class MarkExplicitlyEscaping final : public ContextualMismatch {
|
class MarkExplicitlyEscaping final : public ContextualMismatch {
|
||||||
MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs,
|
MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||||
|
|||||||
@@ -841,6 +841,27 @@ bool LabelingFailure::diagnoseAsNote() {
|
|||||||
return false;
|
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() {
|
bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
|
||||||
if (diagnoseParameterUse())
|
if (diagnoseParameterUse())
|
||||||
return true;
|
return true;
|
||||||
@@ -5129,16 +5150,27 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
|
|||||||
auto eltType = getFromType();
|
auto eltType = getFromType();
|
||||||
auto contextualType = getToType();
|
auto contextualType = getToType();
|
||||||
|
|
||||||
Optional<InFlightDiagnostic> diagnostic;
|
auto isFixedToDictionary = [&](ArrayExpr *anchor) {
|
||||||
if (isExpr<ArrayExpr>(anchor)) {
|
return llvm::any_of(getSolution().Fixes, [&](ConstraintFix *fix) {
|
||||||
if (diagnoseMergedLiteralElements())
|
auto *fixAnchor = getAsExpr<ArrayExpr>(fix->getAnchor());
|
||||||
return true;
|
return fixAnchor && fixAnchor == anchor &&
|
||||||
|
fix->getKind() == FixKind::TreatArrayLiteralAsDictionary;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element,
|
bool treatAsDictionary = false;
|
||||||
eltType, contextualType));
|
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>();
|
auto eltLoc = locator->castLastElementTo<LocatorPathElt::TupleElement>();
|
||||||
switch (eltLoc.getIndex()) {
|
switch (eltLoc.getIndex()) {
|
||||||
case 0: // key
|
case 0: // key
|
||||||
|
|||||||
@@ -690,6 +690,18 @@ protected:
|
|||||||
getDiagnosticFor(ContextualTypePurpose context, Type contextualType);
|
getDiagnosticFor(ContextualTypePurpose context, Type contextualType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Diagnose errors related to using an array literal where a
|
||||||
|
/// dictionary is expected.
|
||||||
|
class ArrayLiteralToDictionaryConversionFailure final : public ContextualFailure {
|
||||||
|
public:
|
||||||
|
ArrayLiteralToDictionaryConversionFailure(const Solution &solution,
|
||||||
|
Type arrayTy, Type dictTy,
|
||||||
|
ConstraintLocator *locator)
|
||||||
|
: ContextualFailure(solution, arrayTy, dictTy, locator) {}
|
||||||
|
|
||||||
|
bool diagnoseAsError() override;
|
||||||
|
};
|
||||||
|
|
||||||
/// Diagnose errors related to converting function type which
|
/// Diagnose errors related to converting function type which
|
||||||
/// isn't explicitly '@escaping' to some other type.
|
/// isn't explicitly '@escaping' to some other type.
|
||||||
class NoEscapeFuncToTypeConversionFailure final : public ContextualFailure {
|
class NoEscapeFuncToTypeConversionFailure final : public ContextualFailure {
|
||||||
|
|||||||
@@ -162,6 +162,23 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
|
|||||||
CoerceToCheckedCast(cs, fromType, toType, locator);
|
CoerceToCheckedCast(cs, fromType, toType, locator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TreatArrayLiteralAsDictionary::diagnose(const Solution &solution,
|
||||||
|
bool asNote) const {
|
||||||
|
ArrayLiteralToDictionaryConversionFailure failure(solution,
|
||||||
|
getToType(), getFromType(),
|
||||||
|
getLocator());
|
||||||
|
return failure.diagnose(asNote);
|
||||||
|
};
|
||||||
|
|
||||||
|
TreatArrayLiteralAsDictionary *
|
||||||
|
TreatArrayLiteralAsDictionary::create(ConstraintSystem &cs,
|
||||||
|
Type dictionaryTy, Type arrayTy,
|
||||||
|
ConstraintLocator *locator) {
|
||||||
|
assert(getAsExpr<ArrayExpr>(locator->getAnchor())->getNumElements() <= 1);
|
||||||
|
return new (cs.getAllocator())
|
||||||
|
TreatArrayLiteralAsDictionary(cs, dictionaryTy, arrayTy, locator);
|
||||||
|
};
|
||||||
|
|
||||||
bool MarkExplicitlyEscaping::diagnose(const Solution &solution,
|
bool MarkExplicitlyEscaping::diagnose(const Solution &solution,
|
||||||
bool asNote) const {
|
bool asNote) const {
|
||||||
NoEscapeFuncToTypeConversionFailure failure(solution, getFromType(),
|
NoEscapeFuncToTypeConversionFailure failure(solution, getFromType(),
|
||||||
|
|||||||
@@ -1744,33 +1744,31 @@ namespace {
|
|||||||
auto &DE = CS.getASTContext().Diags;
|
auto &DE = CS.getASTContext().Diags;
|
||||||
auto numElements = expr->getNumElements();
|
auto numElements = expr->getNumElements();
|
||||||
|
|
||||||
if (numElements == 0) {
|
// Empty and single element array literals with dictionary contextual
|
||||||
DE.diagnose(expr->getStartLoc(),
|
// types are fixed during solving, so continue as normal in those
|
||||||
diag::should_use_empty_dictionary_literal)
|
// cases.
|
||||||
.fixItInsert(expr->getEndLoc(), ":");
|
if (numElements > 1) {
|
||||||
|
bool isIniting =
|
||||||
|
CS.getContextualTypePurpose(expr) == CTP_Initialization;
|
||||||
|
DE.diagnose(expr->getStartLoc(), diag::should_use_dictionary_literal,
|
||||||
|
contextualType->lookThroughAllOptionalTypes(), isIniting);
|
||||||
|
|
||||||
|
auto diagnostic =
|
||||||
|
DE.diagnose(expr->getStartLoc(), diag::meant_dictionary_lit);
|
||||||
|
|
||||||
|
// If there is an even number of elements in the array, let's produce
|
||||||
|
// a fix-it which suggests to replace "," with ":" to form a dictionary
|
||||||
|
// literal.
|
||||||
|
if ((numElements & 1) == 0) {
|
||||||
|
const auto commaLocs = expr->getCommaLocs();
|
||||||
|
if (commaLocs.size() == numElements - 1) {
|
||||||
|
for (unsigned i = 0, e = numElements / 2; i != e; ++i)
|
||||||
|
diagnostic.fixItReplace(commaLocs[i * 2], ":");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isIniting =
|
|
||||||
CS.getContextualTypePurpose(expr) == CTP_Initialization;
|
|
||||||
DE.diagnose(expr->getStartLoc(), diag::should_use_dictionary_literal,
|
|
||||||
contextualType->lookThroughAllOptionalTypes(), isIniting);
|
|
||||||
|
|
||||||
auto diagnostic =
|
|
||||||
DE.diagnose(expr->getStartLoc(), diag::meant_dictionary_lit);
|
|
||||||
|
|
||||||
// If there is an even number of elements in the array, let's produce
|
|
||||||
// a fix-it which suggests to replace "," with ":" to form a dictionary
|
|
||||||
// literal.
|
|
||||||
if ((numElements & 1) == 0) {
|
|
||||||
const auto commaLocs = expr->getCommaLocs();
|
|
||||||
if (commaLocs.size() == numElements - 1) {
|
|
||||||
for (unsigned i = 0, e = numElements / 2; i != e; ++i)
|
|
||||||
diagnostic.fixItReplace(commaLocs[i * 2], ":");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto arrayTy = CS.createTypeVariable(locator,
|
auto arrayTy = CS.createTypeVariable(locator,
|
||||||
|
|||||||
@@ -3206,6 +3206,58 @@ repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool repairArrayLiteralUsedAsDictionary(
|
||||||
|
ConstraintSystem &cs, Type arrayType, Type dictType,
|
||||||
|
ConstraintKind matchKind,
|
||||||
|
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
|
||||||
|
ConstraintLocator *loc) {
|
||||||
|
|
||||||
|
if (!cs.isArrayType(arrayType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Determine the ArrayExpr from the locator.
|
||||||
|
auto *expr = getAsExpr(simplifyLocatorToAnchor(loc));
|
||||||
|
if (!expr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (auto *AE = dyn_cast<AssignExpr>(expr))
|
||||||
|
expr = AE->getSrc();
|
||||||
|
|
||||||
|
auto *arrayExpr = dyn_cast<ArrayExpr>(expr);
|
||||||
|
if (!arrayExpr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// This fix currently only handles empty and single-element arrays:
|
||||||
|
// [] => [:] and [1] => [1:_]
|
||||||
|
if (arrayExpr->getNumElements() > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// This fix only applies if the array is used as a dictionary.
|
||||||
|
auto unwrappedDict = dictType->lookThroughAllOptionalTypes();
|
||||||
|
if (unwrappedDict->isTypeVariableOrMember())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!conformsToKnownProtocol(
|
||||||
|
cs.DC, unwrappedDict,
|
||||||
|
KnownProtocolKind::ExpressibleByDictionaryLiteral))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ignore any attempts at promoting the value to an optional as even after
|
||||||
|
// stripping off all optionals above the underlying types don't match (array
|
||||||
|
// vs dictionary).
|
||||||
|
conversionsOrFixes.erase(llvm::remove_if(conversionsOrFixes,
|
||||||
|
[&](RestrictionOrFix &E) {
|
||||||
|
if (auto restriction = E.getRestriction())
|
||||||
|
return *restriction == ConversionRestrictionKind::ValueToOptional;
|
||||||
|
return false;
|
||||||
|
}), conversionsOrFixes.end());
|
||||||
|
|
||||||
|
auto argLoc = cs.getConstraintLocator(arrayExpr);
|
||||||
|
conversionsOrFixes.push_back(TreatArrayLiteralAsDictionary::create(
|
||||||
|
cs, dictType, arrayType, argLoc));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Let's check whether this is an out-of-order argument in binary
|
/// Let's check whether this is an out-of-order argument in binary
|
||||||
/// operator/function with concrete type parameters e.g.
|
/// operator/function with concrete type parameters e.g.
|
||||||
/// `func ^^(x: Int, y: String)` called as `"" ^^ 42` instead of
|
/// `func ^^(x: Int, y: String)` called as `"" ^^ 42` instead of
|
||||||
@@ -3481,6 +3533,11 @@ bool ConstraintSystem::repairFailures(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (repairArrayLiteralUsedAsDictionary(*this, lhs, rhs, matchKind,
|
||||||
|
conversionsOrFixes,
|
||||||
|
getConstraintLocator(locator)))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
if (!anchor)
|
if (!anchor)
|
||||||
return false;
|
return false;
|
||||||
@@ -10185,6 +10242,44 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||||||
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
|
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FixKind::TreatArrayLiteralAsDictionary: {
|
||||||
|
ArrayExpr *AE = getAsExpr<ArrayExpr>(fix->getAnchor());
|
||||||
|
assert(AE);
|
||||||
|
|
||||||
|
// If the array was empty, there's nothing to do.
|
||||||
|
if (AE->getNumElements() == 0)
|
||||||
|
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
|
||||||
|
|
||||||
|
// For arrays with a single element, match the element type to the
|
||||||
|
// dictionary's key type.
|
||||||
|
SmallVector<Type, 2> optionals;
|
||||||
|
auto dictTy = type2->lookThroughAllOptionalTypes(optionals);
|
||||||
|
|
||||||
|
// If the fix is worse than the best solution, there's no point continuing.
|
||||||
|
if (recordFix(fix, optionals.size() + 1))
|
||||||
|
return SolutionKind::Error;
|
||||||
|
|
||||||
|
// Extract the dictionary key type.
|
||||||
|
ProtocolDecl *dictionaryProto =
|
||||||
|
Context.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
|
||||||
|
auto keyAssocTy = dictionaryProto->getAssociatedType(Context.Id_Key);
|
||||||
|
auto valueBaseTy = createTypeVariable(getConstraintLocator(locator),
|
||||||
|
TVO_CanBindToLValue |
|
||||||
|
TVO_CanBindToNoEscape |
|
||||||
|
TVO_CanBindToHole);
|
||||||
|
assignFixedType(valueBaseTy, dictTy);
|
||||||
|
auto dictionaryKeyTy = DependentMemberType::get(valueBaseTy, keyAssocTy);
|
||||||
|
|
||||||
|
// Extract the array element type.
|
||||||
|
auto elemTy = isArrayType(type1);
|
||||||
|
|
||||||
|
ConstraintLocator *elemLoc = getConstraintLocator(AE->getElement(0));
|
||||||
|
ConstraintKind kind = isDictionaryType(dictTy)
|
||||||
|
? ConstraintKind::Conversion
|
||||||
|
: ConstraintKind::Equal;
|
||||||
|
return matchTypes(*elemTy, dictionaryKeyTy, kind, subflags, elemLoc);
|
||||||
|
}
|
||||||
|
|
||||||
case FixKind::ContextualMismatch: {
|
case FixKind::ContextualMismatch: {
|
||||||
auto impact = 1;
|
auto impact = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,53 @@ var _: MyDictionary<String, Int>? = ["foo" : 1.0] // expected-error {{cannot co
|
|||||||
|
|
||||||
// <rdar://problem/24058895> QoI: Should handle [] in dictionary contexts better
|
// <rdar://problem/24058895> QoI: Should handle [] in dictionary contexts better
|
||||||
var _: [Int: Int] = [] // expected-error {{use [:] to get an empty dictionary literal}} {{22-22=:}}
|
var _: [Int: Int] = [] // expected-error {{use [:] to get an empty dictionary literal}} {{22-22=:}}
|
||||||
|
var _ = useDictStringInt([]) // expected-error {{use [:] to get an empty dictionary literal}} {{27-27=:}}
|
||||||
|
var _: [[Int: Int]] = [[]] // expected-error {{use [:] to get an empty dictionary literal}} {{25-25=:}}
|
||||||
|
var _: [[Int: Int]?] = [[]] // expected-error {{use [:] to get an empty dictionary literal}} {{26-26=:}}
|
||||||
|
var assignDict = [1: 2]
|
||||||
|
assignDict = [] // expected-error {{use [:] to get an empty dictionary literal}} {{15-15=:}}
|
||||||
|
|
||||||
|
var _: [Int: Int] = [1] // expected-error {{dictionary of type '[Int : Int]' cannot be initialized with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{23-23=: <#value#>}}
|
||||||
|
|
||||||
|
var _: [Float: Int] = [1] // expected-error {{dictionary of type '[Float : Int]' cannot be initialized with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{25-25=: <#value#>}}
|
||||||
|
|
||||||
|
var _: [Int: Int] = ["foo"] // expected-error {{dictionary of type '[Int : Int]' cannot be initialized with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{27-27=: <#value#>}}
|
||||||
|
// expected-error@-2 {{cannot convert value of type 'String' to expected dictionary key type 'Int'}}
|
||||||
|
|
||||||
|
var _ = useDictStringInt(["Key"]) // expected-error {{dictionary of type 'DictStringInt' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{32-32=: <#value#>}}
|
||||||
|
|
||||||
|
var _ = useDictStringInt([4]) // expected-error {{dictionary of type 'DictStringInt' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{28-28=: <#value#>}}
|
||||||
|
// expected-error@-2 {{cannot convert value of type 'Int' to expected dictionary key type 'DictStringInt.Key' (aka 'String')}}
|
||||||
|
|
||||||
|
var _: [[Int: Int]] = [[5]] // expected-error {{dictionary of type '[Int : Int]' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{26-26=: <#value#>}}
|
||||||
|
|
||||||
|
var _: [[Int: Int]] = [["bar"]] // expected-error {{dictionary of type '[Int : Int]' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{30-30=: <#value#>}}
|
||||||
|
// expected-error@-2 {{cannot convert value of type 'String' to expected dictionary key type 'Int'}}
|
||||||
|
|
||||||
|
assignDict = [1] // expected-error {{dictionary of type '[Int : Int]' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{16-16=: <#value#>}}
|
||||||
|
|
||||||
|
assignDict = [""] // expected-error {{dictionary of type '[Int : Int]' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{17-17=: <#value#>}}
|
||||||
|
// expected-error@-2 {{cannot convert value of type 'String' to expected dictionary key type 'Int'}}
|
||||||
|
|
||||||
|
func arrayLiteralDictionaryMismatch<T>(a: inout T) where T: ExpressibleByDictionaryLiteral, T.Key == Int, T.Value == Int {
|
||||||
|
a = [] // expected-error {{use [:] to get an empty dictionary literal}} {{8-8=:}}
|
||||||
|
|
||||||
|
a = [1] // expected-error {{dictionary of type 'T' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{9-9=: <#value#>}}
|
||||||
|
|
||||||
|
a = [""] // expected-error {{dictionary of type 'T' cannot be used with array literal}}
|
||||||
|
// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{10-10=: <#value#>}}
|
||||||
|
// expected-error@-2 {{cannot convert value of type 'String' to expected dictionary key type 'Int'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class A { }
|
class A { }
|
||||||
|
|||||||
@@ -14,6 +14,18 @@
|
|||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=UNAMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=UNAMBIGUOUSCLOSURE_ARG
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=UNAMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=UNAMBIGUOUSCLOSURE_ARG
|
||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG
|
||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG_RETURN | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG_RETURN
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG_RETURN | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG_RETURN
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_OPTIONAL | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_NESTED | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_ASSIGN | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_INDIRECT | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_INDIRECT_NESTED | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_INDIRECT_CALL | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_INDIRECT_CALL_OPT | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_INDIRECT_RETURN | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_GENERIC | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_KEY
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_TUPLE | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_TUPLE
|
||||||
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=PARSED_AS_ARRAY_ARRAY | %FileCheck %s --check-prefix=PARSED_AS_ARRAY_TUPLE
|
||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_FOO | %FileCheck %s --check-prefix=OVERLOADEDFUNC_FOO
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_FOO | %FileCheck %s --check-prefix=OVERLOADEDFUNC_FOO
|
||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_BAR | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_BAR | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR
|
||||||
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGLABEL | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH
|
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGLABEL | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH
|
||||||
@@ -165,6 +177,45 @@ takesAnonClosure { TestRelations.#^AMBIGUOUSCLOSURE_ARG_RETURN^# }
|
|||||||
// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}}
|
// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}}
|
||||||
// AMBIGUOUSCLOSURE_ARG_RETURN: End completions
|
// AMBIGUOUSCLOSURE_ARG_RETURN: End completions
|
||||||
|
|
||||||
|
func takesDictAB(_ x: [A: B]) {}
|
||||||
|
func takesOptDictAB(_ x: [A: B]?) {}
|
||||||
|
func overloadedGivesAorB(_ x: A) -> A {}
|
||||||
|
func overloadedGivesAorB(_ x: B) -> B {}
|
||||||
|
var assignDict: [A : B] = [:]
|
||||||
|
|
||||||
|
let _: [A : B] = [TestRelations.#^PARSED_AS_ARRAY^#]
|
||||||
|
let _: [A : B]? = [TestRelations.#^PARSED_AS_ARRAY_OPTIONAL^#]
|
||||||
|
let _: [[A : B]] = [[TestRelations.#^PARSED_AS_ARRAY_NESTED^#]]
|
||||||
|
assignDict = [TestRelations.#^PARSED_AS_ARRAY_ASSIGN^#]
|
||||||
|
let _: [A: B] = [overloadedGivesAorB(TestRelations.#^PARSED_AS_ARRAY_INDIRECT^#)]
|
||||||
|
let _: [[A: B]] = [[overloadedGivesAorB(TestRelations.#^PARSED_AS_ARRAY_INDIRECT_NESTED^#)]]
|
||||||
|
takesDictAB([overloadedGivesAorB(TestRelations.#^PARSED_AS_ARRAY_INDIRECT_CALL^#)]);
|
||||||
|
takesOptDictAB([overloadedGivesAorB(TestRelations.#^PARSED_AS_ARRAY_INDIRECT_CALL_OPT^#)]);
|
||||||
|
func testReturnLiteralMismatch() -> [A: B] { return [overloadedGivesAorB(TestRelations.#^PARSED_AS_ARRAY_INDIRECT_RETURN^#)] }
|
||||||
|
func arrayLiteralDictionaryMismatch<T>(a: inout T) where T: ExpressibleByDictionaryLiteral, T.Key == A, T.Value == B {
|
||||||
|
a = [TestRelations.#^PARSED_AS_ARRAY_GENERIC^#]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PARSED_AS_ARRAY_KEY: Begin completions, 6 items
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Keyword[self]/CurrNominal: self[#TestRelations.Type#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Keyword/CurrNominal: Type[#TestRelations.Type#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: a[#A#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Decl[StaticVar]/CurrNominal: b[#B#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Decl[StaticVar]/CurrNominal: ab[#(A, B)#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_KEY: End completions
|
||||||
|
|
||||||
|
let _: [(A, B) : B] = [TestRelations.#^PARSED_AS_ARRAY_TUPLE^#]
|
||||||
|
let _: [(A, B)] = [TestRelations.#^PARSED_AS_ARRAY_ARRAY^#]
|
||||||
|
// PARSED_AS_ARRAY_TUPLE: Begin completions, 6 items
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Keyword[self]/CurrNominal: self[#TestRelations.Type#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Keyword/CurrNominal: Type[#TestRelations.Type#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Decl[StaticVar]/CurrNominal: a[#A#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Decl[StaticVar]/CurrNominal: b[#B#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: ab[#(A, B)#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}}
|
||||||
|
// PARSED_AS_ARRAY_TUPLE: End completions
|
||||||
|
|
||||||
|
|
||||||
func testMissingArgs() {
|
func testMissingArgs() {
|
||||||
enum Foo { case foo }
|
enum Foo { case foo }
|
||||||
@@ -344,4 +395,3 @@ CreateThings {
|
|||||||
}
|
}
|
||||||
Thing. // ErrorExpr
|
Thing. // ErrorExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user