Skip to content

Commit 13487ed

Browse files
authored
[Typechecker] Diagnose key paths with contextual type but no leading dot (#30164)
1 parent 9718363 commit 13487ed

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,9 @@ ERROR(expr_swift_keypath_invalid_component,none,
577577
"invalid component of Swift key path", ())
578578
ERROR(expr_swift_keypath_not_starting_with_type,none,
579579
"a Swift key path must begin with a type", ())
580+
ERROR(expr_swift_keypath_not_starting_with_dot,none,
581+
"a Swift key path with contextual root must begin with a leading dot",
582+
())
580583
ERROR(expr_smart_keypath_value_covert_to_contextual_type,none,
581584
"key path value type %0 cannot be converted to contextual type %1",
582585
(Type, Type))

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,16 +1774,31 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) {
17741774
auto traversePath = [&](Expr *expr, bool isInParsedPath,
17751775
bool emitErrors = true) {
17761776
Expr *outermostExpr = expr;
1777+
// We can end up in scenarios where the key path has contextual type,
1778+
// but is missing a leading dot. This can happen when we have an
1779+
// implicit TypeExpr or an implicit DeclRefExpr.
1780+
auto diagnoseMissingDot = [&]() {
1781+
DE.diagnose(expr->getLoc(),
1782+
diag::expr_swift_keypath_not_starting_with_dot)
1783+
.fixItInsert(expr->getStartLoc(), ".");
1784+
};
17771785
while (1) {
17781786
// Base cases: we've reached the top.
17791787
if (auto TE = dyn_cast<TypeExpr>(expr)) {
17801788
assert(!isInParsedPath);
17811789
rootType = TE->getTypeRepr();
1790+
if (TE->isImplicit()) {
1791+
diagnoseMissingDot();
1792+
}
17821793
return;
17831794
} else if (isa<KeyPathDotExpr>(expr)) {
17841795
assert(isInParsedPath);
17851796
// Nothing here: the type is either the root, or is inferred.
17861797
return;
1798+
} else if (expr->isImplicit() && isa<DeclRefExpr>(expr)) {
1799+
assert(!isInParsedPath);
1800+
diagnoseMissingDot();
1801+
return;
17871802
}
17881803

17891804
// Recurring cases:

test/expr/unary/keypath/keypath.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,25 @@ func sr11562() {
864864
// expected-error@-1 {{subscript index of type '(Int, Int)' in a key path must be Hashable}}
865865
}
866866

867+
// SR-12290: Ban keypaths with contextual root and without a leading dot
868+
struct SR_12290 {
869+
let property: [Int] = []
870+
let kp1: KeyPath<SR_12290, Int> = \property.count // expected-error {{a Swift key path with contextual root must begin with a leading dot}}{{38-38=.}}
871+
let kp2: KeyPath<SR_12290, Int> = \.property.count // Ok
872+
let kp3: KeyPath<SR_12290, Int> = \SR_12290.property.count // Ok
873+
874+
func foo1(_: KeyPath<SR_12290, Int> = \property.count) {} // expected-error {{a Swift key path with contextual root must begin with a leading dot}}{{42-42=.}}
875+
func foo2(_: KeyPath<SR_12290, Int> = \.property.count) {} // Ok
876+
func foo3(_: KeyPath<SR_12290, Int> = \SR_12290.property.count) {} // Ok
877+
878+
func foo4<T>(_: KeyPath<SR_12290, T>) {}
879+
func useFoo4() {
880+
foo4(\property.count) // expected-error {{a Swift key path with contextual root must begin with a leading dot}}{{11-11=.}}
881+
foo4(\.property.count) // Ok
882+
foo4(\SR_12290.property.count) // Ok
883+
}
884+
}
885+
867886
func testSyntaxErrors() { // expected-note{{}}
868887
_ = \. ; // expected-error{{expected member name following '.'}}
869888
_ = \.a ;

0 commit comments

Comments
 (0)