mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Diagnostics: Ported tuple mismatch diagnostic to new diagnostic framework
This commit is contained in:
@@ -1147,9 +1147,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
|
||||
if (auto fromTT = fromType->getAs<TupleType>())
|
||||
if (auto toTT = toType->getAs<TupleType>()) {
|
||||
if (fromTT->getNumElements() != toTT->getNumElements()) {
|
||||
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible_nelts,
|
||||
fromTT, toTT)
|
||||
.highlight(anchor->getSourceRange());
|
||||
auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
|
||||
CS.getConstraintLocator(expr));
|
||||
failure.diagnoseAsError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1166,9 +1166,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
|
||||
// then we have a type error.
|
||||
if (computeTupleShuffle(TEType->castTo<TupleType>()->getElements(),
|
||||
toTT->getElements(), sources)) {
|
||||
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible,
|
||||
fromTT, toTT)
|
||||
.highlight(anchor->getSourceRange());
|
||||
auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
|
||||
CS.getConstraintLocator(expr));
|
||||
failure.diagnoseAsError();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1887,6 +1887,14 @@ void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool TupleContextualFailure::diagnoseAsError() {
|
||||
auto diagnostic = isNumElementsMismatch()
|
||||
? diag::tuple_types_not_convertible_nelts
|
||||
: diag::tuple_types_not_convertible;
|
||||
emitDiagnostic(getAnchor()->getLoc(), diagnostic, getFromType(), getToType());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AutoClosureForwardingFailure::diagnoseAsError() {
|
||||
auto path = getLocator()->getPath();
|
||||
assert(!path.empty());
|
||||
|
||||
@@ -759,6 +759,23 @@ private:
|
||||
void tryComputedPropertyFixIts(Expr *expr) const;
|
||||
};
|
||||
|
||||
/// Diagnose mismatches relating to tuple destructuring.
|
||||
class TupleContextualFailure final : public ContextualFailure {
|
||||
public:
|
||||
TupleContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
ConstraintLocator *locator)
|
||||
: ContextualFailure(root, cs, lhs, rhs, locator) {}
|
||||
|
||||
bool diagnoseAsError() override;
|
||||
|
||||
bool isNumElementsMismatch() const {
|
||||
auto lhsTy = dyn_cast<TupleType>(getFromType().getPointer());
|
||||
auto rhsTy = dyn_cast<TupleType>(getToType().getPointer());
|
||||
assert(lhsTy && rhsTy);
|
||||
return lhsTy->getElements().size() != rhsTy->getElements().size();
|
||||
}
|
||||
};
|
||||
|
||||
/// Diagnose situations when @autoclosure argument is passed to @autoclosure
|
||||
/// parameter directly without calling it first.
|
||||
class AutoClosureForwardingFailure final : public FailureDiagnostic {
|
||||
|
||||
@@ -229,6 +229,18 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
|
||||
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
|
||||
}
|
||||
|
||||
bool AllowTupleTypeMismatch::diagnose(Expr *root, bool asNote) const {
|
||||
auto failure = TupleContextualFailure(root, getConstraintSystem(), LHS, RHS,
|
||||
getLocator());
|
||||
return failure.diagnose(asNote);
|
||||
}
|
||||
|
||||
AllowTupleTypeMismatch *
|
||||
AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
ConstraintLocator *locator) {
|
||||
return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator);
|
||||
}
|
||||
|
||||
bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const {
|
||||
auto failure = GenericArgumentsMismatchFailure(root, getConstraintSystem(),
|
||||
getActual(), getRequired(),
|
||||
|
||||
@@ -136,6 +136,10 @@ enum class FixKind : uint8_t {
|
||||
/// referenced constructor must be required.
|
||||
AllowInvalidInitRef,
|
||||
|
||||
/// Allow a tuple to be destructured with a mismatched number of
|
||||
/// elements, or mismatched types.
|
||||
AllowTupleTypeMismatch,
|
||||
|
||||
/// Allow an invalid member access on a value of protocol type as if
|
||||
/// that protocol type were a generic constraint requiring conformance
|
||||
/// to that protocol.
|
||||
@@ -862,6 +866,25 @@ private:
|
||||
ConstraintLocator *locator);
|
||||
};
|
||||
|
||||
class AllowTupleTypeMismatch final : public ConstraintFix {
|
||||
Type LHS, RHS;
|
||||
|
||||
AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
ConstraintLocator *locator)
|
||||
: ConstraintFix(cs, FixKind::AllowTupleTypeMismatch, locator), LHS(lhs),
|
||||
RHS(rhs) {}
|
||||
|
||||
public:
|
||||
static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs,
|
||||
Type rhs, ConstraintLocator *locator);
|
||||
|
||||
std::string getName() const override {
|
||||
return "allow invalid tuple destructuring";
|
||||
}
|
||||
|
||||
bool diagnose(Expr *root, bool asNote = false) const override;
|
||||
};
|
||||
|
||||
class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef {
|
||||
AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType,
|
||||
ValueDecl *member, DeclName name,
|
||||
|
||||
@@ -2382,6 +2382,13 @@ bool ConstraintSystem::repairFailures(
|
||||
getConstraintLocator(locator));
|
||||
conversionsOrFixes.push_back(fix);
|
||||
}
|
||||
|
||||
if (purpose == CTP_Initialization && lhs->is<TupleType>() &&
|
||||
rhs->is<TupleType>()) {
|
||||
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
|
||||
getConstraintLocator(locator));
|
||||
conversionsOrFixes.push_back(fix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2413,6 +2420,11 @@ bool ConstraintSystem::repairFailures(
|
||||
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
|
||||
*this, lhs, rhs, getConstraintLocator(locator)));
|
||||
}
|
||||
if (lhs->is<TupleType>() && rhs->is<TupleType>()) {
|
||||
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
|
||||
getConstraintLocator(locator));
|
||||
conversionsOrFixes.push_back(fix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6951,6 +6963,32 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
||||
return matchTypes(type1, type2, matchKind, subflags, locator);
|
||||
}
|
||||
|
||||
case FixKind::AllowTupleTypeMismatch: {
|
||||
auto lhs = dyn_cast<TupleType>(type1.getPointer());
|
||||
auto rhs = dyn_cast<TupleType>(type2.getPointer());
|
||||
auto lhsLarger = lhs->getElements().size() >= rhs->getElements().size();
|
||||
auto larger = lhsLarger ? lhs : rhs;
|
||||
auto smaller = lhsLarger ? rhs : lhs;
|
||||
if (lhs && rhs && getContextualTypePurpose() == CTP_Initialization) {
|
||||
// Match up the tuple type elements, and match any
|
||||
for (unsigned i = 0; i < larger->getElements().size(); ++i) {
|
||||
auto largerTy = larger->getElement(i).getType();
|
||||
if (i < smaller->getElements().size()) {
|
||||
auto smallerTy = smaller->getElement(i).getType();
|
||||
if (smallerTy->isTypeVariableOrMember() ||
|
||||
largerTy->isTypeVariableOrMember())
|
||||
addConstraint(ConstraintKind::Bind, largerTy, smallerTy,
|
||||
getConstraintLocator(locator));
|
||||
} else {
|
||||
addConstraint(ConstraintKind::Defaultable, largerTy,
|
||||
getASTContext().TheAnyType,
|
||||
getConstraintLocator(locator));
|
||||
}
|
||||
}
|
||||
}
|
||||
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
|
||||
}
|
||||
|
||||
case FixKind::InsertCall:
|
||||
case FixKind::RemoveReturn:
|
||||
case FixKind::AddConformance:
|
||||
|
||||
@@ -19,5 +19,5 @@ struct S: P {
|
||||
typealias R = T3
|
||||
|
||||
static let foo: (T1, (R) -> T2) = bind()
|
||||
// expected-error@-1 {{cannot convert value of type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') to specified type '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
|
||||
// expected-error@-1 {{tuple type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') is not convertible to tuple '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func test_varname_binding() {
|
||||
var (d, e) = (c.1, c.0)
|
||||
var ((), (g1, g2), h) = ((), (e, d), e)
|
||||
var (j, k, l) = callee1()
|
||||
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _)', tuples have a different number of elements}}
|
||||
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _, _, _)', tuples have a different number of elements}}
|
||||
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
|
||||
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, Any)', tuples have a different number of elements}}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -139,6 +139,7 @@ func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int {
|
||||
|
||||
// Error recovery.
|
||||
func testfunc2 (_: ((), Int) -> Int) -> Int {}
|
||||
func makeTuple() -> (String, Int) { return ("foo", 42) }
|
||||
func errorRecovery() {
|
||||
testfunc2({ $0 + 1 }) // expected-error {{contextual closure type '((), Int) -> Int' expects 2 arguments, but 1 was used in closure body}}
|
||||
|
||||
@@ -149,12 +150,14 @@ func errorRecovery() {
|
||||
var a: Int = .hello // expected-error {{type 'Int' has no member 'hello'}}
|
||||
var b: union1 = .bar // ok
|
||||
var c: union1 = .xyz // expected-error {{type 'union1' has no member 'xyz'}}
|
||||
var d: (Int,Int,Int) = (1,2) // expected-error {{cannot convert value of type '(Int, Int)' to specified type '(Int, Int, Int)'}}
|
||||
var e: (Int,Int) = (1, 2, 3) // expected-error {{cannot convert value of type '(Int, Int, Int)' to specified type '(Int, Int)'}}
|
||||
var f: (Int,Int) = (1, 2, f : 3) // expected-error {{cannot convert value of type '(Int, Int, f: Int)' to specified type '(Int, Int)'}}
|
||||
var d: (Int,Int,Int) = (1,2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Int)', tuples have a different number of elements}}
|
||||
var e: (Int,Int) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
|
||||
var f: (Int,Int) = (1, 2, f : 3) // expected-error {{'(Int, Int, f: Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
|
||||
|
||||
// <rdar://problem/22426860> CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698
|
||||
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(_, _, _)', tuples have a different number of elements}}
|
||||
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Any)', tuples have a different number of elements}}
|
||||
var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
|
||||
var i: (Bool, Bool) = makeTuple() // expected-error {{tuple type '(String, Int)' is not convertible to tuple '(Bool, Bool)'}}
|
||||
}
|
||||
|
||||
func acceptsInt(_ x: Int) {}
|
||||
@@ -185,7 +188,7 @@ func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) {
|
||||
func test5() {
|
||||
let a: (Int, Int) = (1,2)
|
||||
var
|
||||
_: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}}
|
||||
_: ((Int) -> Int, Int) = a // expected-error {{tuple type '(Int, Int)' is not convertible to tuple '((Int) -> Int, Int)'}}
|
||||
|
||||
|
||||
let c: (a: Int, b: Int) = (1,2)
|
||||
|
||||
Reference in New Issue
Block a user