Skip to content

Commit 1f07cba

Browse files
committed
[Macros] Diagnose attached and freestanding declaration macros that produce
something other than a declaration. The validation code already diagnosed all sorts of invalid declarations, but it was ignoring AST nodes that aren't declarations at all.
1 parent d1436b3 commit 1f07cba

File tree

5 files changed

+72
-5
lines changed

5 files changed

+72
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7284,7 +7284,7 @@ ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled,
72847284
ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
72857285
"expected macro expansion to produce an expression", ())
72867286
ERROR(expected_macro_expansion_decls,PointsToFirstBadToken,
7287-
"expected macro expansion to produce declarations", ())
7287+
"expected macro expansion to produce a declaration", ())
72887288
ERROR(macro_undefined,PointsToFirstBadToken,
72897289
"no macro named %0", (Identifier))
72907290
ERROR(external_macro_not_found,none,

lib/AST/Decl.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11281,8 +11281,24 @@ void MacroExpansionDecl::forEachExpandedNode(
1128111281
return;
1128211282
auto startLoc = sourceMgr.getLocForBufferStart(*bufferID);
1128311283
auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc);
11284-
for (auto node : sourceFile->getTopLevelItems())
11284+
11285+
auto *macro = dyn_cast<MacroDecl>(getMacroRef().getDecl());
11286+
auto roles = macro->getMacroRoles();
11287+
11288+
for (auto node : sourceFile->getTopLevelItems()) {
11289+
// The assumption here is that macros can only have a single
11290+
// freestanding macro role. Expression macros can only produce
11291+
// expressions, declaration macros can only produce declarations,
11292+
// and code item macros can produce expressions, declarations, and
11293+
// statements.
11294+
if (roles.contains(MacroRole::Expression) && !node.is<Expr *>())
11295+
continue;
11296+
11297+
if (roles.contains(MacroRole::Declaration) && !node.is<Decl *>())
11298+
continue;
11299+
1128511300
callback(node);
11301+
}
1128611302
}
1128711303

1128811304
/// Adjust the declaration context to find a point in the context hierarchy

lib/Sema/TypeCheckMacros.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,18 @@ static void validateMacroExpansion(SourceFile *expansionBuffer,
688688
introducedNameSet.count(MacroDecl::getArbitraryName()));
689689
};
690690

691-
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
691+
for (auto item : expansionBuffer->getTopLevelItems()) {
692+
auto *decl = item.dyn_cast<Decl *>();
693+
if (!decl) {
694+
if (role != MacroRole::CodeItem) {
695+
auto &ctx = expansionBuffer->getASTContext();
696+
ctx.Diags.diagnose(item.getStartLoc(),
697+
diag::expected_macro_expansion_decls);
698+
}
699+
700+
continue;
701+
}
702+
692703
// Certain macro roles can generate special declarations.
693704
if ((isa<AccessorDecl>(decl) && role == MacroRole::Accessor) ||
694705
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance)) {
@@ -1156,11 +1167,16 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
11561167
return llvm::None;
11571168

11581169
MacroDecl *macro = cast<MacroDecl>(med->getMacroRef().getDecl());
1170+
auto macroRoles = macro->getMacroRoles();
1171+
assert(macroRoles.contains(MacroRole::Declaration) ||
1172+
macroRoles.contains(MacroRole::CodeItem));
11591173
DeclContext *dc = med->getDeclContext();
11601174

11611175
validateMacroExpansion(macroSourceFile, macro,
11621176
/*attachedTo*/nullptr,
1163-
MacroRole::Declaration);
1177+
macroRoles.contains(MacroRole::Declaration) ?
1178+
MacroRole::Declaration :
1179+
MacroRole::CodeItem);
11641180

11651181
PrettyStackTraceDecl debugStack(
11661182
"type checking expanded declaration macro", med);

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,3 +2054,20 @@ extension RequiredDefaultInitMacro: MemberMacro {
20542054
return [ decl ]
20552055
}
20562056
}
2057+
2058+
public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
2059+
public static func expansion(
2060+
of node: some FreestandingMacroExpansionSyntax,
2061+
in context: some MacroExpansionContext
2062+
) throws -> [DeclSyntax] {
2063+
return ["guard true else { return }"]
2064+
}
2065+
2066+
public static func expansion(
2067+
of node: AttributeSyntax,
2068+
providingPeersOf declaration: some DeclSyntaxProtocol,
2069+
in context: some MacroExpansionContext
2070+
) throws -> [DeclSyntax] {
2071+
return ["if true { return }"]
2072+
}
2073+
}

test/Macros/macro_expand.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -I %t -DIMPORT_MACRO_LIBRARY
1414

1515
// RUN: not %target-swift-frontend -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -serialize-diagnostics-path %t/macro_expand.dia %s -emit-macro-expansion-files no-diagnostics -Rmacro-loading > %t/macro-printing.txt
16-
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS %s
16+
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS -dump-input=always %s
1717

1818
// RUN: %FileCheck %s --check-prefix CHECK-MACRO-PRINTED < %t/macro-printing.txt
1919

@@ -127,6 +127,24 @@ struct Bad {}
127127
// CHECK-DIAGS: END CONTENTS OF FILE
128128
#endif
129129

130+
@freestanding(declaration)
131+
macro accidentalCodeItem() = #externalMacro(module: "MacroDefinition", type: "FakeCodeItemMacro")
132+
133+
@attached(peer)
134+
macro AccidentalCodeItem() = #externalMacro(module: "MacroDefinition", type: "FakeCodeItemMacro")
135+
136+
#if TEST_DIAGNOSTICS
137+
func invalidDeclarationMacro() {
138+
#accidentalCodeItem
139+
// expected-note@-1 {{in expansion of macro 'accidentalCodeItem' here}}
140+
// CHECK-DIAGS: @__swiftmacro_9MacroUser018invalidDeclarationA0yyF18accidentalCodeItemfMf0_.swift:1:1: error: expected macro expansion to produce a declaration
141+
142+
@AccidentalCodeItem struct S {}
143+
// expected-note@-1 {{in expansion of macro 'AccidentalCodeItem' on struct 'S' here}}
144+
// CHECK-DIAGS: @__swiftmacro_9MacroUser018invalidDeclarationA0yyF1SL_18AccidentalCodeItemfMp_.swift:1:1: error: expected macro expansion to produce a declaration
145+
}
146+
#endif
147+
130148
@freestanding(expression) macro customFileID() -> String = #externalMacro(module: "MacroDefinition", type: "FileIDMacro")
131149
@freestanding(expression) macro fileID<T: ExpressibleByStringLiteral>() -> T = #externalMacro(module: "MacroDefinition", type: "FileIDMacro")
132150
@freestanding(expression) macro recurse(_: Bool) = #externalMacro(module: "MacroDefinition", type: "RecursiveMacro")

0 commit comments

Comments
 (0)