Skip to content

Commit 9779591

Browse files
[CSDiagnostics] Prevent nested type references in KeyPath components (swiftlang#83625)
This change adds detection for nested type references in KeyPath components and applies the appropriate fix to generate meaningful error messages, following the same pattern already established for method references. The fix ensures that invalid KeyPath references fail gracefully in normal mode and provide helpful diagnostics in diagnostic mode, improving the developer experience when working with KeyPaths. Resolves: swiftlang#83197
1 parent 0e1be77 commit 9779591

File tree

6 files changed

+54
-0
lines changed

6 files changed

+54
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,9 @@ ERROR(expr_keypath_nonescapable_type,none,
681681
ERROR(expr_keypath_not_property,none,
682682
"%select{key path|dynamic key path member lookup}1 cannot refer to %kind0",
683683
(const ValueDecl *, bool))
684+
ERROR(expr_keypath_type_ref,none,
685+
"%select{key path|dynamic key path member lookup}1 cannot refer to type %0",
686+
(const ValueDecl *, bool))
684687
ERROR(expr_keypath_mutating_getter,none,
685688
"%select{key path|dynamic key path member lookup}1 cannot refer to %0, "
686689
"which has a mutating getter",

include/swift/Sema/CSFix.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20272027
AsyncOrThrowsMethod,
20282028
// Allow a reference to an enum case as a key path component.
20292029
EnumCase,
2030+
// Allow a reference to a type as a key path component.
2031+
TypeReference,
20302032
} Kind;
20312033

20322034
ValueDecl *Member;
@@ -2059,6 +2061,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20592061
case RefKind::AsyncOrThrowsMethod:
20602062
return "allow reference to async or throwing method as a key path "
20612063
"component";
2064+
case RefKind::TypeReference:
2065+
return "allow reference to a type as a key path component";
20622066
}
20632067
llvm_unreachable("covered switch");
20642068
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6494,6 +6494,12 @@ bool InvalidAsyncOrThrowsMethodRefInKeyPath::diagnoseAsError() {
64946494
return true;
64956495
}
64966496

6497+
bool InvalidTypeRefInKeyPath::diagnoseAsError() {
6498+
emitDiagnostic(diag::expr_keypath_type_ref, getMember(),
6499+
isForKeyPathDynamicMemberLookup());
6500+
return true;
6501+
}
6502+
64976503
SourceLoc InvalidUseOfAddressOf::getLoc() const {
64986504
auto anchor = getAnchor();
64996505

lib/Sema/CSDiagnostics.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,27 @@ class InvalidAsyncOrThrowsMethodRefInKeyPath final
18901890
bool diagnoseAsError() override;
18911891
};
18921892

1893+
/// Diagnose an attempt to reference a type as a key path component
1894+
/// e.g.
1895+
///
1896+
/// ```swift
1897+
/// struct S {
1898+
/// enum Q {}
1899+
/// }
1900+
///
1901+
/// _ = \S.Type.Q
1902+
/// ```
1903+
class InvalidTypeRefInKeyPath final : public InvalidMemberRefInKeyPath {
1904+
public:
1905+
InvalidTypeRefInKeyPath(const Solution &solution, ValueDecl *member,
1906+
ConstraintLocator *locator)
1907+
: InvalidMemberRefInKeyPath(solution, member, locator) {
1908+
assert(isa<TypeDecl>(member));
1909+
}
1910+
1911+
bool diagnoseAsError() override;
1912+
};
1913+
18931914
/// Diagnose an attempt to reference a member which has a mutating getter as a
18941915
/// key path component e.g.
18951916
///

lib/Sema/CSFix.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,10 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution,
12881288
getLocator());
12891289
return failure.diagnose(asNote);
12901290
}
1291+
case RefKind::TypeReference: {
1292+
InvalidTypeRefInKeyPath failure(solution, Member, getLocator());
1293+
return failure.diagnose(asNote);
1294+
}
12911295
}
12921296
llvm_unreachable("covered switch");
12931297
}
@@ -1386,6 +1390,11 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType,
13861390
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Initializer,
13871391
member, locator);
13881392

1393+
// Referencing types in key path is not currently allowed.
1394+
if (isa<TypeDecl>(member))
1395+
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::TypeReference,
1396+
member, locator);
1397+
13891398
return nullptr;
13901399
}
13911400

test/expr/unary/keypath/keypath.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,3 +1429,14 @@ func testKeyPathInout() {
14291429
takesInoutOpt(\.count)
14301430
takesInoutOpt(\String.count)
14311431
}
1432+
1433+
func testKeypathWithTypeReference() {
1434+
struct S {
1435+
enum Q {
1436+
static let i = 1
1437+
}
1438+
}
1439+
_ = \S.Q.Type.i // okay
1440+
1441+
_ = \S.Type.Q // expected-error {{key path cannot refer to type 'Q'}}
1442+
}

0 commit comments

Comments
 (0)