Skip to content

Commit 05c3ffc

Browse files
committed
[Macros] Validate that extension macro expansions only include names covered
by the macro declaration.
1 parent 8f64bce commit 05c3ffc

File tree

2 files changed

+90
-57
lines changed

2 files changed

+90
-57
lines changed

lib/Sema/TypeCheckMacros.cpp

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

546+
static void diagnoseInvalidDecl(Decl *decl,
547+
MacroDecl *macro,
548+
llvm::function_ref<bool(DeclName)> coversName) {
549+
auto &ctx = decl->getASTContext();
550+
551+
// Diagnose invalid declaration kinds.
552+
if (isa<ImportDecl>(decl) ||
553+
isa<OperatorDecl>(decl) ||
554+
isa<PrecedenceGroupDecl>(decl) ||
555+
isa<MacroDecl>(decl) ||
556+
isa<ExtensionDecl>(decl)) {
557+
decl->diagnose(diag::invalid_decl_in_macro_expansion,
558+
decl->getDescriptiveKind());
559+
decl->setInvalid();
560+
561+
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
562+
extension->setExtendedNominal(nullptr);
563+
}
564+
565+
return;
566+
}
567+
568+
// Diagnose `@main` types.
569+
if (auto *mainAttr = decl->getAttrs().getAttribute<MainTypeAttr>()) {
570+
ctx.Diags.diagnose(mainAttr->getLocation(),
571+
diag::invalid_main_type_in_macro_expansion);
572+
mainAttr->setInvalid();
573+
}
574+
575+
// Diagnose default literal type overrides.
576+
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
577+
auto name = typeAlias->getBaseIdentifier();
578+
#define EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(_, __, typeName, \
579+
supportsOverride) \
580+
if (supportsOverride && name == makeIdentifier(ctx, typeName)) { \
581+
typeAlias->diagnose(diag::literal_type_in_macro_expansion, \
582+
makeIdentifier(ctx, typeName)); \
583+
typeAlias->setInvalid(); \
584+
return; \
585+
}
586+
#include "swift/AST/KnownProtocols.def"
587+
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
588+
}
589+
590+
// Diagnose value decls with names not covered by the macro
591+
if (auto *value = dyn_cast<ValueDecl>(decl)) {
592+
auto name = value->getName();
593+
594+
// Unique names are always permitted.
595+
if (MacroDecl::isUniqueMacroName(name.getBaseName().userFacingName()))
596+
return;
597+
598+
if (coversName(name)) {
599+
return;
600+
}
601+
602+
value->diagnose(diag::invalid_macro_introduced_name,
603+
name, macro->getBaseName());
604+
}
605+
}
606+
546607
/// Diagnose macro expansions that produce any of the following declarations:
547608
/// - Import declarations
548609
/// - Operator and precedence group declarations
@@ -559,75 +620,33 @@ static void validateMacroExpansion(SourceFile *expansionBuffer,
559620
llvm::SmallVector<DeclName, 2> introducedNames;
560621
macro->getIntroducedNames(role, attachedTo, introducedNames);
561622

562-
llvm::SmallDenseSet<DeclName, 2> coversName(introducedNames.begin(),
563-
introducedNames.end());
623+
llvm::SmallDenseSet<DeclName, 2> introducedNameSet(
624+
introducedNames.begin(), introducedNames.end());
564625

565-
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
566-
auto &ctx = decl->getASTContext();
626+
auto coversName = [&](DeclName name) -> bool {
627+
return (introducedNameSet.count(name) ||
628+
introducedNameSet.count(name.getBaseName()) ||
629+
introducedNameSet.count(MacroDecl::getArbitraryName()));
630+
};
567631

632+
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
568633
// Certain macro roles can generate special declarations.
569634
if ((isa<AccessorDecl>(decl) && role == MacroRole::Accessor) ||
570-
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance) ||
571-
(isa<ExtensionDecl>(decl) && role == MacroRole::Extension)) { // FIXME: Check extension for generated names.
635+
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance)) {
572636
continue;
573637
}
574638

575-
// Diagnose invalid declaration kinds.
576-
if (isa<ImportDecl>(decl) ||
577-
isa<OperatorDecl>(decl) ||
578-
isa<PrecedenceGroupDecl>(decl) ||
579-
isa<MacroDecl>(decl) ||
580-
isa<ExtensionDecl>(decl)) {
581-
decl->diagnose(diag::invalid_decl_in_macro_expansion,
582-
decl->getDescriptiveKind());
583-
decl->setInvalid();
584-
585-
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
586-
extension->setExtendedNominal(nullptr);
639+
if (role == MacroRole::Extension) {
640+
auto *extension = dyn_cast<ExtensionDecl>(decl);
641+
642+
for (auto *member : extension->getMembers()) {
643+
diagnoseInvalidDecl(member, macro, coversName);
587644
}
588645

589646
continue;
590647
}
591648

592-
// Diagnose `@main` types.
593-
if (auto *mainAttr = decl->getAttrs().getAttribute<MainTypeAttr>()) {
594-
ctx.Diags.diagnose(mainAttr->getLocation(),
595-
diag::invalid_main_type_in_macro_expansion);
596-
mainAttr->setInvalid();
597-
}
598-
599-
// Diagnose default literal type overrides.
600-
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
601-
auto name = typeAlias->getBaseIdentifier();
602-
#define EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(_, __, typeName, \
603-
supportsOverride) \
604-
if (supportsOverride && name == makeIdentifier(ctx, typeName)) { \
605-
typeAlias->diagnose(diag::literal_type_in_macro_expansion, \
606-
makeIdentifier(ctx, typeName)); \
607-
typeAlias->setInvalid(); \
608-
continue; \
609-
}
610-
#include "swift/AST/KnownProtocols.def"
611-
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
612-
}
613-
614-
// Diagnose value decls with names not covered by the macro
615-
if (auto *value = dyn_cast<ValueDecl>(decl)) {
616-
auto name = value->getName();
617-
618-
// Unique names are always permitted.
619-
if (MacroDecl::isUniqueMacroName(name.getBaseName().userFacingName()))
620-
continue;
621-
622-
if (coversName.count(name) ||
623-
coversName.count(name.getBaseName()) ||
624-
coversName.count(MacroDecl::getArbitraryName())) {
625-
continue;
626-
}
627-
628-
value->diagnose(diag::invalid_macro_introduced_name,
629-
name, macro->getBaseName());
630-
}
649+
diagnoseInvalidDecl(decl, macro, coversName);
631650
}
632651
}
633652

test/Macros/macro_expand_extensions.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
// RUN: %target-swift-frontend -enable-experimental-feature ExtensionMacros -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking -dump-macro-expansions > %t/expansions-dump.txt 2>&1
77
// RUN: %FileCheck -check-prefix=CHECK-DUMP %s < %t/expansions-dump.txt
88
// RUN: %target-typecheck-verify-swift -enable-experimental-feature ExtensionMacros -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5 -I %t
9+
10+
// RUN: not %target-swift-frontend -enable-experimental-feature ExtensionMacros -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
11+
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS %s
12+
913
// RUN: %target-build-swift -enable-experimental-feature ExtensionMacros -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser -swift-version 5 -emit-tbd -emit-tbd-path %t/MacroUser.tbd -I %t
1014
// RUN: %target-codesign %t/main
1115
// RUN: %target-run %t/main | %FileCheck %s
@@ -106,4 +110,14 @@ func testLocal() {
106110
struct Local<Element> {}
107111
// expected-error@-1{{local type cannot have attached extension macro}}
108112
}
113+
114+
115+
@attached(extension, conformances: P)
116+
macro UndocumentedNamesInExtension() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceViaExtensionMacro")
117+
118+
@UndocumentedNamesInExtension
119+
struct S<Element> {}
120+
// expected-note@-1 {{in expansion of macro 'UndocumentedNamesInExtension' here}}
121+
122+
// CHECK-DIAGS: error: declaration name 'requirement()' is not covered by macro 'UndocumentedNamesInExtension'
109123
#endif

0 commit comments

Comments
 (0)