Skip to content

Commit 8f44a0b

Browse files
theblixguyxedin
authored andcommitted
[TypeChecker] Diagnose key paths with contextual type but no leading dot
If a Swift key path has root type inferred but does not have a leading dot, then diagnose it, because it's not valid. For example: ```swift struct Foo { let property: [Int] = [] let kp: KeyPath<Foo, Int> = \property.count // error } ``` Resolves SR-12290 Resolves rdar://problem/59874355
1 parent 3accb65 commit 8f44a0b

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
@@ -580,6 +580,9 @@ ERROR(expr_swift_keypath_invalid_component,none,
580580
"invalid component of Swift key path", ())
581581
ERROR(expr_swift_keypath_not_starting_with_type,none,
582582
"a Swift key path must begin with a type", ())
583+
ERROR(expr_swift_keypath_not_starting_with_dot,none,
584+
"a Swift key path with contextual root must begin with a leading dot",
585+
())
583586
ERROR(expr_smart_keypath_value_covert_to_contextual_type,none,
584587
"key path value type %0 cannot be converted to contextual type %1",
585588
(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() && !KPE->expectsContextualRoot())
1791+
diagnoseMissingDot();
17821792
return;
17831793
} else if (isa<KeyPathDotExpr>(expr)) {
17841794
assert(isInParsedPath);
17851795
// Nothing here: the type is either the root, or is inferred.
17861796
return;
1797+
} else if (!KPE->expectsContextualRoot() && expr->isImplicit() &&
1798+
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
@@ -874,6 +874,25 @@ func sr11562() {
874874
// expected-error@-1 {{subscript index of type '(Int, Int)' in a key path must be Hashable}}
875875
}
876876

877+
// SR-12290: Ban keypaths with contextual root and without a leading dot
878+
struct SR_12290 {
879+
let property: [Int] = []
880+
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=.}}
881+
let kp2: KeyPath<SR_12290, Int> = \.property.count // Ok
882+
let kp3: KeyPath<SR_12290, Int> = \SR_12290.property.count // Ok
883+
884+
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=.}}
885+
func foo2(_: KeyPath<SR_12290, Int> = \.property.count) {} // Ok
886+
func foo3(_: KeyPath<SR_12290, Int> = \SR_12290.property.count) {} // Ok
887+
888+
func foo4<T>(_: KeyPath<SR_12290, T>) {}
889+
func useFoo4() {
890+
foo4(\property.count) // expected-error {{a Swift key path with contextual root must begin with a leading dot}}{{11-11=.}}
891+
foo4(\.property.count) // Ok
892+
foo4(\SR_12290.property.count) // Ok
893+
}
894+
}
895+
877896
func testSyntaxErrors() { // expected-note{{}}
878897
_ = \. ; // expected-error{{expected member name following '.'}}
879898
_ = \.a ;

0 commit comments

Comments
 (0)