Skip to content

Commit 6fdb548

Browse files
committed
[Diagnostics] Diagnose invalid method references in key path
1 parent d6a3a31 commit 6fdb548

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,3 +2486,9 @@ bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
24862486
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName());
24872487
return true;
24882488
}
2489+
2490+
bool InvalidMethodRefInKeyPath::diagnoseAsError() {
2491+
emitDiagnostic(getLoc(), diag::expr_keypath_not_property, getKind(),
2492+
getName());
2493+
return true;
2494+
}

lib/Sema/CSDiagnostics.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,8 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
10421042
assert(locator->isForKeyPathComponent());
10431043
}
10441044

1045+
DescriptiveDeclKind getKind() const { return Member->getDescriptiveKind(); }
1046+
10451047
DeclName getName() const { return Member->getFullName(); }
10461048

10471049
bool diagnoseAsError() override = 0;
@@ -1098,6 +1100,29 @@ class InvalidMemberWithMutatingGetterInKeyPath final
10981100
bool diagnoseAsError() override;
10991101
};
11001102

1103+
/// Diagnose an attempt to reference a method as a key path component
1104+
/// e.g.
1105+
///
1106+
/// ```swift
1107+
/// struct S {
1108+
/// func foo() -> Int { return 42 }
1109+
/// static func bar() -> Int { return 0 }
1110+
/// }
1111+
///
1112+
/// _ = \S.foo
1113+
/// _ = \S.Type.bar
1114+
/// ```
1115+
class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath {
1116+
public:
1117+
InvalidMethodRefInKeyPath(Expr *root, ConstraintSystem &cs, ValueDecl *method,
1118+
ConstraintLocator *locator)
1119+
: InvalidMemberRefInKeyPath(root, cs, method, locator) {
1120+
assert(isa<FuncDecl>(method));
1121+
}
1122+
1123+
bool diagnoseAsError() override;
1124+
};
1125+
11011126
} // end namespace constraints
11021127
} // end namespace swift
11031128

lib/Sema/CSFix.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,9 @@ bool AllowInvalidRefInKeyPath::diagnose(Expr *root, bool asNote) const {
438438
}
439439

440440
case RefKind::Method: {
441-
return false;
441+
InvalidMethodRefInKeyPath failure(root, getConstraintSystem(), Member,
442+
getLocator());
443+
return failure.diagnose(asNote);
442444
}
443445
}
444446
}

test/expr/unary/keypath/keypath.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,31 @@ func test_keypath_with_mutating_getter() {
776776
}
777777
}
778778

779+
func test_keypath_with_method_refs() {
780+
struct S {
781+
func foo() -> Int { return 42 }
782+
static func bar() -> Int { return 0 }
783+
}
784+
785+
let _: KeyPath<S, Int> = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}}
786+
let _: KeyPath<S, Int> = \.bar // expected-error {{key path cannot refer to static member 'bar()'}}
787+
let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}}
788+
789+
struct A {
790+
func foo() -> B { return B() }
791+
static func faz() -> B { return B() }
792+
}
793+
794+
struct B {
795+
var bar: Int = 42
796+
}
797+
798+
let _: KeyPath<A, Int> = \.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}}
799+
let _: KeyPath<A, Int> = \.faz.bar // expected-error {{key path cannot refer to static member 'faz()'}}
800+
let _ = \A.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}}
801+
let _ = \A.Type.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}}
802+
}
803+
779804
func testSyntaxErrors() { // expected-note{{}}
780805
_ = \. ; // expected-error{{expected member name following '.'}}
781806
_ = \.a ;

0 commit comments

Comments
 (0)