Skip to content

Commit 9b722b9

Browse files
authored
Merge pull request #64097 from hborla/macro-expansion-validation
[Macros] Diagnose macro expansions containing invalid declarations.
2 parents 46da4e9 + 2f81706 commit 9b722b9

File tree

11 files changed

+243
-6
lines changed

11 files changed

+243
-6
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8463,6 +8463,11 @@ class MacroDecl : public GenericContext, public ValueDecl {
84638463
void getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
84648464
SmallVectorImpl<DeclName> &names) const;
84658465

8466+
/// Returns a DeclName that represents arbitrary names.
8467+
static DeclName getArbitraryName() {
8468+
return DeclName();
8469+
}
8470+
84668471
/// Retrieve the definition of this macro.
84678472
MacroDefinition getDefinition() const;
84688473

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6909,6 +6909,18 @@ ERROR(external_macro_arg_not_type_name,none,
69096909
"the external macro's %select{module|type}0", (unsigned))
69106910
ERROR(attached_declaration_macro_not_supported,none,
69116911
"attached declaration macros are not yet supported", ())
6912+
ERROR(invalid_decl_in_macro_expansion,none,
6913+
"macro expansion cannot introduce %0",
6914+
(DescriptiveDeclKind))
6915+
ERROR(invalid_main_type_in_macro_expansion,none,
6916+
"macro expansion cannot introduce '@main' type",
6917+
())
6918+
ERROR(literal_type_in_macro_expansion,none,
6919+
"macro expansion cannot introduce default literal type %0",
6920+
(Identifier))
6921+
ERROR(invalid_macro_introduced_name,none,
6922+
"declaration name %0 is not covered by macro %1",
6923+
(DeclName, DeclName))
69126924

69136925
//------------------------------------------------------------------------------
69146926
// MARK: Move Only Errors

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10122,7 +10122,7 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
1012210122
}
1012310123

1012410124
case MacroIntroducedDeclNameKind::Arbitrary:
10125-
// FIXME: Indicate that the macro covers arbitrary names.
10125+
names.push_back(MacroDecl::getArbitraryName());
1012610126
break;
1012710127
}
1012810128
}

lib/Sema/TypeCheckMacros.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,104 @@ ExpandPeerMacroRequest::evaluate(Evaluator &evaluator, Decl *decl) const {
476476
return decl->getASTContext().AllocateCopy(bufferIDs);
477477
}
478478

479+
static Identifier makeIdentifier(ASTContext &ctx, StringRef name) {
480+
return ctx.getIdentifier(name);
481+
}
482+
483+
static Identifier makeIdentifier(ASTContext &ctx, std::nullptr_t) {
484+
return Identifier();
485+
}
486+
487+
/// Diagnose macro expansions that produce any of the following declarations:
488+
/// - Import declarations
489+
/// - Operator and precedence group declarations
490+
/// - Macro declarations
491+
/// - Extensions
492+
/// - Types with `@main` attributes
493+
/// - Top-level default literal type overrides
494+
/// - Value decls with names not covered by the macro declaration.
495+
static void validateMacroExpansion(SourceFile *expansionBuffer,
496+
MacroDecl *macro,
497+
ValueDecl *attachedTo,
498+
MacroRole role) {
499+
// Gather macro-introduced names
500+
llvm::SmallVector<DeclName, 2> introducedNames;
501+
macro->getIntroducedNames(role, attachedTo, introducedNames);
502+
503+
llvm::SmallDenseSet<DeclName, 2> coversName(introducedNames.begin(),
504+
introducedNames.end());
505+
506+
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
507+
auto &ctx = decl->getASTContext();
508+
509+
// Certain macro roles can generate special declarations.
510+
if ((isa<AccessorDecl>(decl) && role == MacroRole::Accessor) ||
511+
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance)) {
512+
continue;
513+
}
514+
515+
// Diagnose invalid declaration kinds.
516+
if (isa<ImportDecl>(decl) ||
517+
isa<OperatorDecl>(decl) ||
518+
isa<PrecedenceGroupDecl>(decl) ||
519+
isa<MacroDecl>(decl) ||
520+
isa<ExtensionDecl>(decl)) {
521+
decl->diagnose(diag::invalid_decl_in_macro_expansion,
522+
decl->getDescriptiveKind());
523+
decl->setInvalid();
524+
525+
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
526+
extension->setExtendedNominal(nullptr);
527+
}
528+
529+
continue;
530+
}
531+
532+
// Diagnose `@main` types.
533+
if (auto *mainAttr = decl->getAttrs().getAttribute<MainTypeAttr>()) {
534+
ctx.Diags.diagnose(mainAttr->getLocation(),
535+
diag::invalid_main_type_in_macro_expansion);
536+
mainAttr->setInvalid();
537+
}
538+
539+
// Diagnose default literal type overrides.
540+
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
541+
auto name = typeAlias->getBaseIdentifier();
542+
#define EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(_, __, typeName, \
543+
supportsOverride) \
544+
if (supportsOverride && name == makeIdentifier(ctx, typeName)) { \
545+
typeAlias->diagnose(diag::literal_type_in_macro_expansion, \
546+
makeIdentifier(ctx, typeName)); \
547+
typeAlias->setInvalid(); \
548+
continue; \
549+
}
550+
#include "swift/AST/KnownProtocols.def"
551+
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
552+
}
553+
554+
// Diagnose value decls with names not covered by the macro
555+
if (auto *value = dyn_cast<ValueDecl>(decl)) {
556+
auto baseName = value->getBaseName();
557+
if (baseName.isSpecial()) {
558+
baseName = ctx.getIdentifier(baseName.userFacingName());
559+
}
560+
561+
// $-prefixed names are unique names. These are always allowed.
562+
if (baseName.getIdentifier().hasDollarPrefix()) {
563+
continue;
564+
}
565+
566+
if (coversName.count(baseName) ||
567+
coversName.count(MacroDecl::getArbitraryName())) {
568+
continue;
569+
}
570+
571+
value->diagnose(diag::invalid_macro_introduced_name,
572+
baseName, macro->getBaseName());
573+
}
574+
}
575+
}
576+
479577
/// Determine whether the given source file is from an expansion of the given
480578
/// macro.
481579
static bool isFromExpansionOfMacro(SourceFile *sourceFile, MacroDecl *macro,
@@ -1101,6 +1199,8 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr,
11011199
/*parsingOpts=*/{}, /*isPrimary=*/false);
11021200
macroSourceFile->setImports(declSourceFile->getImports());
11031201

1202+
validateMacroExpansion(macroSourceFile, macro,
1203+
dyn_cast<ValueDecl>(attachedTo), role);
11041204
return macroSourceFile;
11051205
}
11061206

stdlib/public/core/Macros.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public macro dsohandle() -> UnsafeRawPointer = Builtin.DSOHandleMacro
8686
/// case standard
8787
/// }
8888
/// }
89-
@attached(member)
89+
@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary)
9090
@attached(conformance)
9191
public macro OptionSet<RawType>() =
9292
#externalMacro(module: "SwiftMacros", type: "OptionSetMacro")

test/Macros/Inputs/macro_library.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ public struct ObservationRegistrar<Subject: Observable> {
2323
public func register<Value>(observable: Subject, didSet: KeyPath<Subject, Value>) {}
2424
}
2525

26-
@attached(member)
26+
@attached(
27+
member,
28+
names: named(_registrar), named(addObserver), named(removeObserver), named(withTransaction), named(Storage), named(_storage)
29+
)
2730
@attached(memberAttribute)
2831
public macro Observable() = #externalMacro(module: "MacroDefinition", type: "ObservableMacro")
2932

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,25 @@ public struct AddMembers: MemberMacro {
494494
}
495495
}
496496

497+
public struct AddArbitraryMembers: MemberMacro {
498+
public static func expansion(
499+
of node: AttributeSyntax,
500+
providingMembersOf decl: some DeclGroupSyntax,
501+
in context: some MacroExpansionContext
502+
) throws -> [DeclSyntax] {
503+
guard let identified = decl.asProtocol(IdentifiedDeclSyntax.self) else {
504+
return []
505+
}
506+
507+
let parentName = identified.identifier.trimmed
508+
return [
509+
"struct \(parentName)1 {}",
510+
"struct \(parentName)2 {}",
511+
"struct \(parentName)3 {}",
512+
]
513+
}
514+
}
515+
497516
/// Implementation of the `wrapStoredProperties` macro, which can be
498517
/// used to apply an attribute to all of the stored properties of a type.
499518
///
@@ -745,6 +764,38 @@ public struct AddCompletionHandler: PeerMacro {
745764
}
746765
}
747766

767+
public struct InvalidMacro: PeerMacro {
768+
public static func expansion(
769+
of node: AttributeSyntax,
770+
providingPeersOf declaration: some DeclSyntaxProtocol,
771+
in context: some MacroExpansionContext
772+
) throws -> [DeclSyntax] {
773+
return [
774+
"import Swift",
775+
"precedencegroup MyPrecedence {}",
776+
"@attached(member) macro myMacro()",
777+
"extension Int {}",
778+
"""
779+
@main
780+
struct MyMain {
781+
static func main() {}
782+
}
783+
""",
784+
"typealias Array = Void",
785+
"typealias Dictionary = Void",
786+
"typealias BooleanLiteralType = Void",
787+
"typealias ExtendedGraphemeClusterType = Void",
788+
"typealias FloatLiteralType = Void",
789+
"typealias IntegerLiteralType = Void",
790+
"typealias StringLiteralType = Void",
791+
"typealias UnicodeScalarType = Void",
792+
"typealias _ColorLiteralType = Void",
793+
"typealias _ImageLiteralType = Void",
794+
"typealias _FileReferenceLiteralType = Void",
795+
]
796+
}
797+
}
798+
748799
public struct WrapInType: PeerMacro {
749800
public static func expansion(
750801
of node: AttributeSyntax,

test/Macros/composed_macro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// REQUIRES: OS=macosx
1111

1212
@attached(memberAttribute)
13-
@attached(member)
13+
@attached(member, names: named(_storage))
1414
macro myTypeWrapper() = #externalMacro(module: "MacroDefinition", type: "TypeWrapperMacro")
1515
@attached(accessor) macro accessViaStorage() = #externalMacro(module: "MacroDefinition", type: "AccessViaStorageMacro")
1616

test/Macros/macro_expand.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,55 @@
2626
// FIXME: Swift parser is not enabled on Linux CI yet.
2727
// REQUIRES: OS=macosx
2828

29+
#if TEST_DIAGNOSTICS
30+
@attached(peer)
31+
macro Invalid() = #externalMacro(module: "MacroDefinition", type: "InvalidMacro")
32+
33+
@Invalid
34+
struct Bad {}
35+
// expected-note@-1 18 {{in expansion of macro 'Invalid' here}}
36+
37+
// CHECK-DIAGS: error: macro expansion cannot introduce import
38+
// CHECK-DIAGS: error: macro expansion cannot introduce precedence group
39+
// CHECK-DIAGS: error: macro expansion cannot introduce macro
40+
// CHECK-DIAGS: error: macro expansion cannot introduce extension
41+
// CHECK-DIAGS: error: macro expansion cannot introduce '@main' type
42+
// CHECK-DIAGS: error: declaration name 'MyMain' is not covered by macro 'Invalid'
43+
// CHECK-DIAGS: error: declaration name 'Array' is not covered by macro 'Invalid'
44+
// CHECK-DIAGS: error: declaration name 'Dictionary' is not covered by macro 'Invalid'
45+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'BooleanLiteralType'
46+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'ExtendedGraphemeClusterType'
47+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'FloatLiteralType'
48+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'IntegerLiteralType'
49+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'StringLiteralType'
50+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type 'UnicodeScalarType'
51+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type '_ColorLiteralType'
52+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type '_ImageLiteralType'
53+
// CHECK-DIAGS: error: macro expansion cannot introduce default literal type '_FileReferenceLiteralType'
54+
55+
// CHECK-DIAGS: CONTENTS OF FILE @__swiftmacro_9MacroUser3BadV7InvalidfMp_.swift
56+
// CHECK-DIAGS: import Swift
57+
// CHECK-DIAGS: precedencegroup MyPrecedence {}
58+
// CHECK-DIAGS: @attached(member) macro myMacro()
59+
// CHECK-DIAGS: extension Int {}
60+
// CHECK-DIAGS: @main
61+
// CHECK-DIAGS: struct MyMain {
62+
// CHECK-DIAGS: static func main() {}
63+
// CHECK-DIAGS: }
64+
// CHECK-DIAGS: typealias Array = Void
65+
// CHECK-DIAGS: typealias Dictionary = Void
66+
// CHECK-DIAGS: typealias BooleanLiteralType = Void
67+
// CHECK-DIAGS: typealias ExtendedGraphemeClusterType = Void
68+
// CHECK-DIAGS: typealias FloatLiteralType = Void
69+
// CHECK-DIAGS: typealias IntegerLiteralType = Void
70+
// CHECK-DIAGS: typealias StringLiteralType = Void
71+
// CHECK-DIAGS: typealias UnicodeScalarType = Void
72+
// CHECK-DIAGS: typealias _ColorLiteralType = Void
73+
// CHECK-DIAGS: typealias _ImageLiteralType = Void
74+
// CHECK-DIAGS: typealias _FileReferenceLiteralType = Void
75+
// CHECK-DIAGS: END CONTENTS OF FILE
76+
#endif
77+
2978
@freestanding(expression) macro customFileID() -> String = #externalMacro(module: "MacroDefinition", type: "FileIDMacro")
3079
@freestanding(expression) macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro")
3180
@freestanding(expression) macro fileID<T: ExpressibleByStringLiteral>() -> T = #externalMacro(module: "MacroDefinition", type: "FileIDMacro")

test/Macros/macro_expand_conformances.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ requireEquatable(S2())
4040
requireHashable(S2())
4141

4242
@attached(conformance)
43-
@attached(member)
43+
@attached(member, names: named(requirement))
4444
macro DelegatedConformance() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceMacro")
4545

4646
protocol P {

test/Macros/macro_expand_synthesized_members.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
// FIXME: Swift parser is not enabled on Linux CI yet.
1010
// REQUIRES: OS=macosx
1111

12-
@attached(member) macro addMembers() = #externalMacro(module: "MacroDefinition", type: "AddMembers")
12+
@attached(
13+
member,
14+
names: named(Storage), named(storage), named(getStorage), named(method), named(`init`)
15+
)
16+
macro addMembers() = #externalMacro(module: "MacroDefinition", type: "AddMembers")
1317

1418
@addMembers
1519
struct S {
@@ -25,6 +29,19 @@ let s = S()
2529
// CHECK: Storage
2630
s.useSynthesized()
2731

32+
@attached(member, names: arbitrary)
33+
macro addArbitraryMembers() = #externalMacro(module: "MacroDefinition", type: "AddArbitraryMembers")
34+
35+
@addArbitraryMembers
36+
struct MyType {}
37+
38+
// CHECK: MyType1
39+
// CHECK: MyType2
40+
// CHECK: MyType3
41+
print(MyType.MyType1.self)
42+
print(MyType.MyType2.self)
43+
print(MyType.MyType3.self)
44+
2845
@attached(
2946
member,
3047
names: named(RawValue), named(rawValue), named(`init`)

0 commit comments

Comments
 (0)