diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index c1efd380736..7f070c85dee 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -285,6 +285,10 @@ enum class FixKind : uint8_t { /// Allow expressions to reference invalid declarations by turning /// them into holes. AllowRefToInvalidDecl, + + /// Treat empty and single-element array literals as if they were incomplete + /// dictionary literals when used as such. + TreatArrayLiteralAsDictionary, }; class ConstraintFix { @@ -551,6 +555,25 @@ public: 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'. class MarkExplicitlyEscaping final : public ContextualMismatch { MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 7e0a5ece2b1..0f711343d49 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -841,6 +841,27 @@ bool LabelingFailure::diagnoseAsNote() { return false; } +bool ArrayLiteralToDictionaryConversionFailure::diagnoseAsError() { + ArrayExpr *AE = getAsExpr(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 diagnostic; - if (isExpr(anchor)) { - if (diagnoseMergedLiteralElements()) - return true; + auto isFixedToDictionary = [&](ArrayExpr *anchor) { + return llvm::any_of(getSolution().Fixes, [&](ConstraintFix *fix) { + auto *fixAnchor = getAsExpr(fix->getAnchor()); + return fixAnchor && fixAnchor == anchor && + fix->getKind() == FixKind::TreatArrayLiteralAsDictionary; + }); + }; - diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element, - eltType, contextualType)); + bool treatAsDictionary = false; + Optional diagnostic; + if (auto *AE = getAsExpr(anchor)) { + if (!(treatAsDictionary = isFixedToDictionary(AE))) { + if (diagnoseMergedLiteralElements()) + return true; + + diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element, + eltType, contextualType)); + } } - if (isExpr(anchor)) { + if (treatAsDictionary || isExpr(anchor)) { auto eltLoc = locator->castLastElementTo(); switch (eltLoc.getIndex()) { case 0: // key diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index b2ee422ccd7..07c314c7048 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -690,6 +690,18 @@ protected: 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 /// isn't explicitly '@escaping' to some other type. class NoEscapeFuncToTypeConversionFailure final : public ContextualFailure { diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 851425c9e00..9548272e9d8 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -162,6 +162,23 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs, 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(locator->getAnchor())->getNumElements() <= 1); + return new (cs.getAllocator()) + TreatArrayLiteralAsDictionary(cs, dictionaryTy, arrayTy, locator); +}; + bool MarkExplicitlyEscaping::diagnose(const Solution &solution, bool asNote) const { NoEscapeFuncToTypeConversionFailure failure(solution, getFromType(), diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f9ae54a4524..9b941e37231 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1744,33 +1744,31 @@ namespace { auto &DE = CS.getASTContext().Diags; auto numElements = expr->getNumElements(); - if (numElements == 0) { - DE.diagnose(expr->getStartLoc(), - diag::should_use_empty_dictionary_literal) - .fixItInsert(expr->getEndLoc(), ":"); + // Empty and single element array literals with dictionary contextual + // types are fixed during solving, so continue as normal in those + // cases. + 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; } - - 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, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4400a7dfbe4..b7da884260d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3206,6 +3206,58 @@ repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType, return true; } +static bool repairArrayLiteralUsedAsDictionary( + ConstraintSystem &cs, Type arrayType, Type dictType, + ConstraintKind matchKind, + SmallVectorImpl &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(expr)) + expr = AE->getSrc(); + + auto *arrayExpr = dyn_cast(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 /// operator/function with concrete type parameters e.g. /// `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 (!anchor) return false; @@ -10185,6 +10242,44 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } + case FixKind::TreatArrayLiteralAsDictionary: { + ArrayExpr *AE = getAsExpr(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 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: { auto impact = 1; diff --git a/test/Constraints/dictionary_literal.swift b/test/Constraints/dictionary_literal.swift index 8d92e97487d..6fcd80d4fa5 100644 --- a/test/Constraints/dictionary_literal.swift +++ b/test/Constraints/dictionary_literal.swift @@ -71,6 +71,53 @@ var _: MyDictionary? = ["foo" : 1.0] // expected-error {{cannot co // QoI: Should handle [] in dictionary contexts better 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(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 { } diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index c8b6f3fe890..be6b51dbf27 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -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=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=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_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 @@ -165,6 +177,45 @@ takesAnonClosure { TestRelations.#^AMBIGUOUSCLOSURE_ARG_RETURN^# } // AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}} // 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(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() { enum Foo { case foo } @@ -344,4 +395,3 @@ CreateThings { } Thing. // ErrorExpr } -