Skip to content

Commit 04ea8b0

Browse files
committed
[Macros] Diagnose macros attached to declarations they cannot apply to.
1 parent 05c3ffc commit 04ea8b0

11 files changed

+122
-17
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7268,6 +7268,9 @@ ERROR(local_extension_macro,none,
72687268
ERROR(extension_macro_invalid_conformance,none,
72697269
"invalid protocol conformance %0 in extension macro",
72707270
(Type))
7271+
ERROR(macro_attached_to_invalid_decl,none,
7272+
"'%0' macro cannot be attached to %1",
7273+
(StringRef, DescriptiveDeclKind))
72717274

72727275
ERROR(macro_resolve_circular_reference, none,
72737276
"circular reference resolving %select{freestanding|attached}0 macro %1",

lib/Sema/TypeCheckAttr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3665,10 +3665,17 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
36653665
Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
36663666

36673667
if (!nominal) {
3668+
if (attr->isInvalid())
3669+
return;
3670+
36683671
// Try resolving an attached macro attribute.
3669-
auto *macro = D->getResolvedMacro(attr);
3670-
if (macro || !attr->isValid())
3672+
if (auto *macro = D->getResolvedMacro(attr)) {
3673+
for (auto *roleAttr : macro->getAttrs().getAttributes<MacroRoleAttr>()) {
3674+
diagnoseInvalidAttachedMacro(roleAttr->getMacroRole(), D);
3675+
}
3676+
36713677
return;
3678+
}
36723679

36733680
// Diagnose errors.
36743681

lib/Sema/TypeCheckMacros.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,51 @@ static Identifier makeIdentifier(ASTContext &ctx, std::nullptr_t) {
543543
return Identifier();
544544
}
545545

546+
bool swift::diagnoseInvalidAttachedMacro(MacroRole role,
547+
Decl *attachedTo) {
548+
switch (role) {
549+
case MacroRole::Expression:
550+
case MacroRole::Declaration:
551+
case MacroRole::CodeItem:
552+
llvm_unreachable("Invalid macro role for attached macro");
553+
554+
case MacroRole::Accessor:
555+
// Only var decls and subscripts have accessors.
556+
if (isa<AbstractStorageDecl>(attachedTo) && !isa<ParamDecl>(attachedTo))
557+
return false;
558+
559+
break;
560+
561+
case MacroRole::MemberAttribute:
562+
case MacroRole::Member:
563+
// Nominal types and extensions can have members.
564+
if (isa<NominalTypeDecl>(attachedTo) || isa<ExtensionDecl>(attachedTo))
565+
return false;
566+
567+
break;
568+
569+
case MacroRole::Peer:
570+
// Peer macros are allowed on everything except parameters.
571+
if (!isa<ParamDecl>(attachedTo))
572+
return false;
573+
574+
break;
575+
576+
case MacroRole::Conformance:
577+
case MacroRole::Extension:
578+
// Only primary declarations of nominal types
579+
if (isa<NominalTypeDecl>(attachedTo))
580+
return false;
581+
582+
break;
583+
}
584+
585+
attachedTo->diagnose(diag::macro_attached_to_invalid_decl,
586+
getMacroRoleString(role),
587+
attachedTo->getDescriptiveKind());
588+
return true;
589+
}
590+
546591
static void diagnoseInvalidDecl(Decl *decl,
547592
MacroDecl *macro,
548593
llvm::function_ref<bool(DeclName)> coversName) {

lib/Sema/TypeCheckMacros.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ bool accessorMacroOnlyIntroducesObservers(
9090
bool accessorMacroIntroducesInitAccessor(
9191
MacroDecl *macro, const MacroRoleAttr *attr);
9292

93+
/// Diagnose an error if the given macro role does not apply
94+
/// to the declaration kind of \c attachedTo.
95+
bool diagnoseInvalidAttachedMacro(MacroRole role,
96+
Decl *attachedTo);
97+
9398
} // end namespace swift
9499

95100
#endif /* SWIFT_SEMA_TYPECHECKMACROS_H */

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,16 @@ public struct EmptyMacro: MemberMacro {
13141314
}
13151315
}
13161316

1317+
public struct EmptyPeerMacro: PeerMacro {
1318+
public static func expansion(
1319+
of node: AttributeSyntax,
1320+
providingPeersOf declaration: some DeclSyntaxProtocol,
1321+
in context: some MacroExpansionContext
1322+
) throws -> [DeclSyntax] {
1323+
return []
1324+
}
1325+
}
1326+
13171327
public struct EquatableMacro: ConformanceMacro {
13181328
public static func expansion(
13191329
of node: AttributeSyntax,

test/Macros/accessor_macros.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,7 @@ struct MyBrokenStruct {
112112
}
113113
}
114114

115+
@myPropertyWrapper
116+
struct CannotHaveAccessors {}
117+
// CHECK-DIAGS: 'accessor' macro cannot be attached to struct
115118
#endif

test/Macros/attached_macros_diags.swift

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
// REQUIRES: swift_swift_parser
22

3-
// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name MacrosTest
3+
// RUN: %empty-directory(%t)
4+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -parse-as-library -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
5+
// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -disable-availability-checking -module-name MacrosTest
46

5-
@attached(accessor) macro m1() = #externalMacro(module: "MyMacros", type: "Macro1")
6-
// expected-warning@-1{{external macro implementation type 'MyMacros.Macro1' could not be found for macro 'm1()'}}
7-
// expected-note@-2{{'m1()' declared here}}
7+
@attached(peer) macro m1() = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
88

9-
@attached(accessor) macro m2(_: Int) = #externalMacro(module: "MyMacros", type: "Macro2")
10-
// expected-warning@-1{{external macro implementation type 'MyMacros.Macro2' could not be found for macro 'm2'}}
11-
// expected-note@-2{{candidate has partially matching parameter list (Int)}}
12-
// expected-note@-3{{candidate expects value of type 'Int' for parameter #1 (got 'String')}}
9+
@attached(peer) macro m2(_: Int) = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
10+
// expected-note@-1{{candidate has partially matching parameter list (Int)}}
11+
// expected-note@-2{{candidate expects value of type 'Int' for parameter #1 (got 'String')}}
1312

14-
@attached(accessor) macro m2(_: Double) = #externalMacro(module: "MyMacros", type: "Macro2")
15-
// expected-warning@-1{{external macro implementation type 'MyMacros.Macro2' could not be found for macro 'm2'}}
16-
// expected-note@-2{{candidate has partially matching parameter list (Double)}}
17-
// expected-note@-3{{candidate expects value of type 'Double' for parameter #1 (got 'String')}}
13+
@attached(peer) macro m2(_: Double) = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
14+
// expected-note@-1{{candidate has partially matching parameter list (Double)}}
15+
// expected-note@-2{{candidate expects value of type 'Double' for parameter #1 (got 'String')}}
1816

19-
@attached(accessor) macro m3(message: String) = #externalMacro(module: "MyMacros", type: "Macro3")
20-
// expected-warning@-1{{external macro implementation type 'MyMacros.Macro3' could not be found for macro 'm3(message:)'}}
17+
@attached(peer) macro m3(message: String) = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
2118

2219
@freestanding(expression) macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MyMacros", type: "StringifyMacro")
2320
// expected-warning@-1{{external macro implementation type 'MyMacros.StringifyMacro' could not be found for macro 'stringify'}}
@@ -38,7 +35,12 @@ struct SkipNestedType {
3835

3936
// We select the macro, not the property wrapper.
4037
@m1 var x: Int = 0
41-
// expected-error@-1{{external macro implementation type 'MyMacros.Macro1' could not be found for macro 'm1()'}}
38+
//expected-note@-1{{did you mean 'x'?}}
39+
40+
func test() {
41+
let _: m1<Int> = _x
42+
// expected-error@-1{{cannot find '_x' in scope}}
43+
}
4244
}
4345

4446
struct TestMacroArgs {

test/Macros/macro_expand_attributes.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,13 @@ expansionOrder.expandedMember = 27
140140

141141
// CHECK: setting 28
142142
expansionOrder.originalMember = 28
143+
144+
#if TEST_DIAGNOSTICS
145+
@wrapAllProperties
146+
typealias A = Int
147+
// expected-error@-1{{'memberAttribute' macro cannot be attached to type alias}}
148+
149+
@wrapAllProperties
150+
func noMembers() {}
151+
// expected-error@-1{{'memberAttribute' macro cannot be attached to global function}}
152+
#endif

test/Macros/macro_expand_extensions.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ func testLocal() {
111111
// expected-error@-1{{local type cannot have attached extension macro}}
112112
}
113113

114+
@DelegatedConformance
115+
typealias A = Int
116+
// expected-error@-1 {{'extension' macro cannot be attached to type alias}}
117+
118+
@DelegatedConformance
119+
extension Int {}
120+
// expected-error@-1 {{'extension' macro cannot be attached to extension}}
114121

115122
@attached(extension, conformances: P)
116123
macro UndocumentedNamesInExtension() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceViaExtensionMacro")

test/Macros/macro_expand_peers.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ func testVarPeer() {
214214
_ = Date().value
215215
}
216216

217+
#if TEST_DIAGNOSTICS
218+
// Macros cannot be attached to function parameters
219+
220+
// expected-error@+1{{'peer' macro cannot be attached to parameter}}
221+
func test(@declareVarValuePeer x: Int) {}
222+
#endif
223+
217224
// Stored properties added via peer macros.
218225
@attached(peer, names: named(_foo))
219226
macro AddPeerStoredProperty() =

test/Macros/macro_expand_synthesized_members.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ macro addMembers() = #externalMacro(module: "MacroDefinition", type: "AddMembers
1919
)
2020
macro addMembersQuotedInit() = #externalMacro(module: "MacroDefinition", type: "AddMembers")
2121

22+
#if TEST_DIAGNOSTICS
23+
@addMembers
24+
import Swift
25+
// expected-error@-1 {{'member' macro cannot be attached to import}}
26+
#endif
27+
2228
@addMembers
2329
struct S {
2430
func useSynthesized() {

0 commit comments

Comments
 (0)