Skip to content

Commit fe1d926

Browse files
[CSDiagnostics] Prevent nested type references in KeyPath components
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: #83197
1 parent 663ec93 commit fe1d926

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
@@ -2024,6 +2024,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20242024
AsyncOrThrowsMethod,
20252025
// Allow a reference to an enum case as a key path component.
20262026
EnumCase,
2027+
// Allow a reference to a type as a key path component.
2028+
TypeReference,
20272029
} Kind;
20282030

20292031
ValueDecl *Member;
@@ -2056,6 +2058,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
20562058
case RefKind::AsyncOrThrowsMethod:
20572059
return "allow reference to async or throwing method as a key path "
20582060
"component";
2061+
case RefKind::TypeReference:
2062+
return "allow reference to a type as a key path component";
20592063
}
20602064
llvm_unreachable("covered switch");
20612065
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6485,6 +6485,12 @@ bool InvalidAsyncOrThrowsMethodRefInKeyPath::diagnoseAsError() {
64856485
return true;
64866486
}
64876487

6488+
bool InvalidTypeRefInKeyPath::diagnoseAsError() {
6489+
emitDiagnostic(diag::expr_keypath_type_ref, getMember(),
6490+
isForKeyPathDynamicMemberLookup());
6491+
return true;
6492+
}
6493+
64886494
SourceLoc InvalidUseOfAddressOf::getLoc() const {
64896495
auto anchor = getAnchor();
64906496

lib/Sema/CSDiagnostics.h

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

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

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)