diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6a32586c265c7..b0274e8a18629 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1727,6 +1727,9 @@ ERROR(invalid_dynamic_member_lookup_type,none, "'@dynamicMemberLookup' requires %0 to have a " "'subscript(dynamicMember:)' method that accepts either " "'ExpressibleByStringLiteral' or a key path", (Type)) +NOTE(add_subscript_method, none, + "add to %0 a 'subscript(dynamicMember:)' method that accepts " + "either 'ExpressibleByStringLiteral' or a key path", (Type)) NOTE(invalid_dynamic_member_subscript, none, "add an explicit argument label to this subscript to satisfy " "the '@dynamicMemberLookup' requirement", ()) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 4a089d9a0a44a..6269cd20b6fa8 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2106,6 +2106,7 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, return bool(getKeyPathTypeForDynamicMemberLookup(decl, ignoreLabel)); } + /// The @dynamicMemberLookup attribute is only allowed on types that have at /// least one subscript member declared like this: /// @@ -2161,8 +2162,14 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { return isValidDynamicMemberLookupSubscript(cand, /*ignoreLabel*/ true); }); - // If there were no potentially valid candidates, then throw an error. + // If there were no potentially valid candidates, then throw an error and emit a fix-it. if (newCandidates.empty()) { + + // Emit a fix-it to suggest the user to add a subscript method. + auto &d = ctx.Diags; + d.diagnose(decl->getLoc(), diag::add_subscript_method, type) + .fixItReplace(decl->getLoc(), "dynamicMember"); + emitInvalidTypeDiagnostic(attr->getLocation()); return; } diff --git a/test/attr/attr_dynamic_member_lookup.swift b/test/attr/attr_dynamic_member_lookup.swift index c921c4aee0414..587d07abc5c4d 100644 --- a/test/attr/attr_dynamic_member_lookup.swift +++ b/test/attr/attr_dynamic_member_lookup.swift @@ -191,7 +191,7 @@ func NotAllowedOnFunc() {} // expected-error @+1 {{'@dynamicMemberLookup' requires 'InvalidBase' to have a 'subscript(dynamicMember:)' method that accepts either 'ExpressibleByStringLiteral' or a key path}} @dynamicMemberLookup -class InvalidBase {} +class InvalidBase {} // expected-note {{add to 'InvalidBase' a 'subscript(dynamicMember:)' method that accepts either 'ExpressibleByStringLiteral' or a key path}} class InvalidDerived : InvalidBase { subscript(dynamicMember: String) -> Int { get {}} } @@ -787,6 +787,7 @@ do { // https://github.com/apple/swift/issues/52957 + @dynamicMemberLookup struct S1_52957 { subscript(dynamicMember: String) -> String { // expected-error {{'@dynamicMemberLookup' requires 'S1_52957' to have a 'subscript(dynamicMember:)' method that accepts either 'ExpressibleByStringLiteral' or a key path}}