Skip to content

Commit c9a86ac

Browse files
committed
[Macros] Diagnose macro expansions containing invalid declarations, such as
new macro decls or value decls whose names are not covered by the macro.
1 parent 7324307 commit c9a86ac

File tree

6 files changed

+92
-4
lines changed

6 files changed

+92
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6909,6 +6909,15 @@ 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 %1",
6914+
(DescriptiveDeclKind))
6915+
ERROR(invalid_main_type_in_macro_expansion,none,
6916+
"macro expansion cannot introduce '@main' type'",
6917+
())
6918+
ERROR(invalid_macro_introduced_name,none,
6919+
"declaration name %0 is not covered by macro %1",
6920+
(DeclName, DeclName))
69126921

69136922
//------------------------------------------------------------------------------
69146923
// MARK: Move Only Errors

lib/Sema/TypeCheckMacros.cpp

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

479+
/// Diagnose macro expansions that produce any of the following declarations:
480+
/// - Import declarations
481+
/// - Operator and precedence group declarations
482+
/// - Macro declarations
483+
/// - Extensions
484+
/// - Types with `@main` attributes
485+
/// - Top-level default literal type overrides
486+
/// - Value decls with names not covered by the macro declaration.
487+
static void validateMacroExpansion(SourceFile *expansionBuffer,
488+
MacroDecl *macro,
489+
ValueDecl *attachedTo,
490+
MacroRole role) {
491+
// Gather macro-introduced names
492+
llvm::SmallVector<DeclName, 2> introducedNames;
493+
macro->getIntroducedNames(role, attachedTo, introducedNames);
494+
495+
llvm::SmallDenseSet<DeclName, 2> coversName(introducedNames.begin(),
496+
introducedNames.end());
497+
498+
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
499+
auto &ctx = decl->getASTContext();
500+
501+
// Certain macro roles can generate special declarations.
502+
if ((isa<AccessorDecl>(decl) && role == MacroRole::Accessor) ||
503+
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance)) {
504+
continue;
505+
}
506+
507+
// Diagnose invalid declaration kinds.
508+
if (isa<ImportDecl>(decl) ||
509+
isa<OperatorDecl>(decl) ||
510+
isa<PrecedenceGroupDecl>(decl) ||
511+
isa<MacroDecl>(decl) ||
512+
isa<ExtensionDecl>(decl)) {
513+
decl->diagnose(diag::invalid_decl_in_macro_expansion,
514+
decl->getDescriptiveKind());
515+
}
516+
517+
// Diagnose `@main` types.
518+
if (auto *typeDecl = dyn_cast<TypeDecl>(decl)) {
519+
if (typeDecl->getAttrs().hasAttribute<MainTypeAttr>()) {
520+
typeDecl->diagnose(diag::invalid_main_type_in_macro_expansion);
521+
}
522+
}
523+
524+
// Diagnose default literal type overrides.
525+
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
526+
// TODO
527+
}
528+
529+
// Diagnose value decls with names not covered by the macro
530+
if (auto *value = dyn_cast<ValueDecl>(decl)) {
531+
auto baseName = value->getBaseName();
532+
if (baseName.isSpecial()) {
533+
baseName = ctx.getIdentifier(baseName.userFacingName());
534+
}
535+
536+
// $-prefixed names are unique names. These are always allowed.
537+
if (baseName.getIdentifier().hasDollarPrefix()) {
538+
continue;
539+
}
540+
541+
if (!coversName.count(baseName)) {
542+
value->diagnose(diag::invalid_macro_introduced_name,
543+
baseName, macro->getBaseName());
544+
}
545+
}
546+
}
547+
}
548+
479549
/// Determine whether the given source file is from an expansion of the given
480550
/// macro.
481551
static bool isFromExpansionOfMacro(SourceFile *sourceFile, MacroDecl *macro,
@@ -1102,6 +1172,8 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr,
11021172
/*parsingOpts=*/{}, /*isPrimary=*/false);
11031173
macroSourceFile->setImports(declSourceFile->getImports());
11041174

1175+
validateMacroExpansion(macroSourceFile, macro,
1176+
dyn_cast<ValueDecl>(attachedTo), role);
11051177
return macroSourceFile;
11061178
}
11071179

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/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_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: 5 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 {

0 commit comments

Comments
 (0)