Skip to content

Commit e9867f6

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. (cherry picked from commit 1f07cba)
1 parent a66f28e commit e9867f6

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
@@ -7225,7 +7225,7 @@ ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled,
72257225
ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
72267226
"expected macro expansion to produce an expression", ())
72277227
ERROR(expected_macro_expansion_decls,PointsToFirstBadToken,
7228-
"expected macro expansion to produce declarations", ())
7228+
"expected macro expansion to produce a declaration", ())
72297229
ERROR(macro_undefined,PointsToFirstBadToken,
72307230
"no macro named %0", (Identifier))
72317231
ERROR(external_macro_not_found,none,

lib/AST/Decl.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11035,8 +11035,24 @@ void MacroExpansionDecl::forEachExpandedNode(
1103511035
return;
1103611036
auto startLoc = sourceMgr.getLocForBufferStart(*bufferID);
1103711037
auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc);
11038-
for (auto node : sourceFile->getTopLevelItems())
11038+
11039+
auto *macro = dyn_cast<MacroDecl>(getMacroRef().getDecl());
11040+
auto roles = macro->getMacroRoles();
11041+
11042+
for (auto node : sourceFile->getTopLevelItems()) {
11043+
// The assumption here is that macros can only have a single
11044+
// freestanding macro role. Expression macros can only produce
11045+
// expressions, declaration macros can only produce declarations,
11046+
// and code item macros can produce expressions, declarations, and
11047+
// statements.
11048+
if (roles.contains(MacroRole::Expression) && !node.is<Expr *>())
11049+
continue;
11050+
11051+
if (roles.contains(MacroRole::Declaration) && !node.is<Decl *>())
11052+
continue;
11053+
1103911054
callback(node);
11055+
}
1104011056
}
1104111057

1104211058
/// 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
@@ -687,7 +687,18 @@ static void validateMacroExpansion(SourceFile *expansionBuffer,
687687
introducedNameSet.count(MacroDecl::getArbitraryName()));
688688
};
689689

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

11551166
MacroDecl *macro = cast<MacroDecl>(med->getMacroRef().getDecl());
1167+
auto macroRoles = macro->getMacroRoles();
1168+
assert(macroRoles.contains(MacroRole::Declaration) ||
1169+
macroRoles.contains(MacroRole::CodeItem));
11561170
DeclContext *dc = med->getDeclContext();
11571171

11581172
validateMacroExpansion(macroSourceFile, macro,
11591173
/*attachedTo*/nullptr,
1160-
MacroRole::Declaration);
1174+
macroRoles.contains(MacroRole::Declaration) ?
1175+
MacroRole::Declaration :
1176+
MacroRole::CodeItem);
11611177

11621178
PrettyStackTraceDecl debugStack(
11631179
"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)