Skip to content

Commit 8b3938b

Browse files
authored
Merge pull request #67387 from hborla/5.9-macro-validation
2 parents 2394524 + 8e216f2 commit 8b3938b

11 files changed

+333
-74
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7181,6 +7181,9 @@ ERROR(literal_type_in_macro_expansion,none,
71817181
ERROR(invalid_macro_introduced_name,none,
71827182
"declaration name %0 is not covered by macro %1",
71837183
(DeclName, DeclName))
7184+
ERROR(undocumented_conformance_in_expansion,none,
7185+
"conformance to %0 is not covered by macro %1",
7186+
(Type, DeclName))
71847187
ERROR(invalid_macro_role_for_macro_syntax,none,
71857188
"invalid macro role for %{a freestanding|an attached}0 macro",
71867189
(unsigned))
@@ -7203,6 +7206,9 @@ ERROR(local_extension_macro,none,
72037206
ERROR(extension_macro_invalid_conformance,none,
72047207
"invalid protocol conformance %0 in extension macro",
72057208
(Type))
7209+
ERROR(macro_attached_to_invalid_decl,none,
7210+
"'%0' macro cannot be attached to %1",
7211+
(StringRef, DescriptiveDeclKind))
72067212

72077213
ERROR(macro_resolve_circular_reference, none,
72087214
"circular reference resolving %select{freestanding|attached}0 macro %1",

lib/Sema/TypeCheckAttr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3659,10 +3659,17 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
36593659
Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
36603660

36613661
if (!nominal) {
3662+
if (attr->isInvalid())
3663+
return;
3664+
36623665
// Try resolving an attached macro attribute.
3663-
auto *macro = D->getResolvedMacro(attr);
3664-
if (macro || !attr->isValid())
3666+
if (auto *macro = D->getResolvedMacro(attr)) {
3667+
for (auto *roleAttr : macro->getAttrs().getAttributes<MacroRoleAttr>()) {
3668+
diagnoseInvalidAttachedMacro(roleAttr->getMacroRole(), D);
3669+
}
3670+
36653671
return;
3672+
}
36663673

36673674
// Diagnose errors.
36683675

lib/Sema/TypeCheckMacros.cpp

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

546+
bool swift::diagnoseInvalidAttachedMacro(MacroRole role,
547+
Decl *attachedTo) {
548+
switch (role) {
549+
case MacroRole::Expression:
550+
case MacroRole::Declaration:
551+
case MacroRole::CodeItem:
552+
llvm_unreachable("Invalid macro role for attached macro");
553+
554+
case MacroRole::Accessor:
555+
// Only var decls and subscripts have accessors.
556+
if (isa<AbstractStorageDecl>(attachedTo) && !isa<ParamDecl>(attachedTo))
557+
return false;
558+
559+
break;
560+
561+
case MacroRole::MemberAttribute:
562+
case MacroRole::Member:
563+
// Nominal types and extensions can have members.
564+
if (isa<NominalTypeDecl>(attachedTo) || isa<ExtensionDecl>(attachedTo))
565+
return false;
566+
567+
break;
568+
569+
case MacroRole::Peer:
570+
// Peer macros are allowed on everything except parameters.
571+
if (!isa<ParamDecl>(attachedTo))
572+
return false;
573+
574+
break;
575+
576+
case MacroRole::Conformance:
577+
case MacroRole::Extension:
578+
// Only primary declarations of nominal types
579+
if (isa<NominalTypeDecl>(attachedTo))
580+
return false;
581+
582+
break;
583+
}
584+
585+
attachedTo->diagnose(diag::macro_attached_to_invalid_decl,
586+
getMacroRoleString(role),
587+
attachedTo->getDescriptiveKind());
588+
return true;
589+
}
590+
591+
static void diagnoseInvalidDecl(Decl *decl,
592+
MacroDecl *macro,
593+
llvm::function_ref<bool(DeclName)> coversName) {
594+
auto &ctx = decl->getASTContext();
595+
596+
// Diagnose invalid declaration kinds.
597+
if (isa<ImportDecl>(decl) ||
598+
isa<OperatorDecl>(decl) ||
599+
isa<PrecedenceGroupDecl>(decl) ||
600+
isa<MacroDecl>(decl) ||
601+
isa<ExtensionDecl>(decl)) {
602+
decl->diagnose(diag::invalid_decl_in_macro_expansion,
603+
decl->getDescriptiveKind());
604+
decl->setInvalid();
605+
606+
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
607+
extension->setExtendedNominal(nullptr);
608+
}
609+
610+
return;
611+
}
612+
613+
// Diagnose `@main` types.
614+
if (auto *mainAttr = decl->getAttrs().getAttribute<MainTypeAttr>()) {
615+
ctx.Diags.diagnose(mainAttr->getLocation(),
616+
diag::invalid_main_type_in_macro_expansion);
617+
mainAttr->setInvalid();
618+
}
619+
620+
// Diagnose default literal type overrides.
621+
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
622+
auto name = typeAlias->getBaseIdentifier();
623+
#define EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(_, __, typeName, \
624+
supportsOverride) \
625+
if (supportsOverride && name == makeIdentifier(ctx, typeName)) { \
626+
typeAlias->diagnose(diag::literal_type_in_macro_expansion, \
627+
makeIdentifier(ctx, typeName)); \
628+
typeAlias->setInvalid(); \
629+
return; \
630+
}
631+
#include "swift/AST/KnownProtocols.def"
632+
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
633+
}
634+
635+
// Diagnose value decls with names not covered by the macro
636+
if (auto *value = dyn_cast<ValueDecl>(decl)) {
637+
auto name = value->getName();
638+
639+
// Unique names are always permitted.
640+
if (MacroDecl::isUniqueMacroName(name.getBaseName().userFacingName()))
641+
return;
642+
643+
if (coversName(name)) {
644+
return;
645+
}
646+
647+
value->diagnose(diag::invalid_macro_introduced_name,
648+
name, macro->getBaseName());
649+
}
650+
}
651+
546652
/// Diagnose macro expansions that produce any of the following declarations:
547653
/// - Import declarations
548654
/// - Operator and precedence group declarations
@@ -559,75 +665,33 @@ static void validateMacroExpansion(SourceFile *expansionBuffer,
559665
llvm::SmallVector<DeclName, 2> introducedNames;
560666
macro->getIntroducedNames(role, attachedTo, introducedNames);
561667

562-
llvm::SmallDenseSet<DeclName, 2> coversName(introducedNames.begin(),
563-
introducedNames.end());
668+
llvm::SmallDenseSet<DeclName, 2> introducedNameSet(
669+
introducedNames.begin(), introducedNames.end());
564670

565-
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
566-
auto &ctx = decl->getASTContext();
671+
auto coversName = [&](DeclName name) -> bool {
672+
return (introducedNameSet.count(name) ||
673+
introducedNameSet.count(name.getBaseName()) ||
674+
introducedNameSet.count(MacroDecl::getArbitraryName()));
675+
};
567676

677+
for (auto *decl : expansionBuffer->getTopLevelDecls()) {
568678
// Certain macro roles can generate special declarations.
569679
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.
680+
(isa<ExtensionDecl>(decl) && role == MacroRole::Conformance)) {
572681
continue;
573682
}
574683

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);
684+
if (role == MacroRole::Extension) {
685+
auto *extension = dyn_cast<ExtensionDecl>(decl);
686+
687+
for (auto *member : extension->getMembers()) {
688+
diagnoseInvalidDecl(member, macro, coversName);
587689
}
588690

589691
continue;
590692
}
591693

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-
}
694+
diagnoseInvalidDecl(decl, macro, coversName);
631695
}
632696
}
633697

@@ -1572,6 +1636,50 @@ llvm::Optional<unsigned> swift::expandExtensions(CustomAttr *attr,
15721636
if (auto file = dyn_cast<FileUnit>(
15731637
decl->getDeclContext()->getModuleScopeContext()))
15741638
file->getOrCreateSynthesizedFile().addTopLevelDecl(extension);
1639+
1640+
// Don't validate documented conformances for the 'conformance' role.
1641+
if (role == MacroRole::Conformance)
1642+
continue;
1643+
1644+
// Extension macros can only add conformances that are documented by
1645+
// the `@attached(extension)` attribute.
1646+
for (auto inherited : extension->getInherited()) {
1647+
auto constraint =
1648+
TypeResolution::forInterface(
1649+
extension->getDeclContext(),
1650+
TypeResolverContext::GenericRequirement,
1651+
/*unboundTyOpener*/ nullptr,
1652+
/*placeholderHandler*/ nullptr,
1653+
/*packElementOpener*/ nullptr)
1654+
.resolveType(inherited.getTypeRepr());
1655+
1656+
// Already diagnosed or will be diagnosed later.
1657+
if (constraint->is<ErrorType>() || !constraint->isConstraintType())
1658+
continue;
1659+
1660+
std::function<bool(Type)> isUndocumentedConformance =
1661+
[&](Type constraint) -> bool {
1662+
if (auto *proto = constraint->getAs<ParameterizedProtocolType>())
1663+
return !llvm::is_contained(potentialConformances,
1664+
proto->getProtocol());
1665+
1666+
if (auto *proto = constraint->getAs<ProtocolType>())
1667+
return !llvm::is_contained(potentialConformances,
1668+
proto->getDecl());
1669+
1670+
return llvm::any_of(
1671+
constraint->castTo<ProtocolCompositionType>()->getMembers(),
1672+
isUndocumentedConformance);
1673+
};
1674+
1675+
if (isUndocumentedConformance(constraint)) {
1676+
extension->diagnose(
1677+
diag::undocumented_conformance_in_expansion,
1678+
constraint, macro->getBaseName());
1679+
1680+
extension->setInvalid();
1681+
}
1682+
}
15751683
}
15761684

15771685
return macroSourceFile->getBufferID();

lib/Sema/TypeCheckMacros.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ bool accessorMacroOnlyIntroducesObservers(
8989
bool accessorMacroIntroducesInitAccessor(
9090
MacroDecl *macro, const MacroRoleAttr *attr);
9191

92+
/// Diagnose an error if the given macro role does not apply
93+
/// to the declaration kind of \c attachedTo.
94+
bool diagnoseInvalidAttachedMacro(MacroRole role,
95+
Decl *attachedTo);
96+
9297
} // end namespace swift
9398

9499
#endif /* SWIFT_SEMA_TYPECHECKMACROS_H */

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,16 @@ public struct EmptyMacro: MemberMacro {
13141314
}
13151315
}
13161316

1317+
public struct EmptyPeerMacro: PeerMacro {
1318+
public static func expansion(
1319+
of node: AttributeSyntax,
1320+
providingPeersOf declaration: some DeclSyntaxProtocol,
1321+
in context: some MacroExpansionContext
1322+
) throws -> [DeclSyntax] {
1323+
return []
1324+
}
1325+
}
1326+
13171327
public struct EquatableMacro: ConformanceMacro {
13181328
public static func expansion(
13191329
of node: AttributeSyntax,
@@ -1427,6 +1437,52 @@ public struct DelegatedConformanceViaExtensionMacro: ExtensionMacro {
14271437
}
14281438
}
14291439

1440+
public struct AlwaysAddConformance: ExtensionMacro {
1441+
public static func expansion(
1442+
of node: AttributeSyntax,
1443+
attachedTo decl: some DeclGroupSyntax,
1444+
providingExtensionsOf type: some TypeSyntaxProtocol,
1445+
conformingTo protocols: [TypeSyntax],
1446+
in context: some MacroExpansionContext
1447+
) throws -> [ExtensionDeclSyntax] {
1448+
let decl: DeclSyntax =
1449+
"""
1450+
extension \(raw: type.trimmedDescription): P where Element: P {
1451+
static func requirement() {
1452+
Element.requirement()
1453+
}
1454+
}
1455+
1456+
"""
1457+
1458+
return [
1459+
decl.cast(ExtensionDeclSyntax.self)
1460+
]
1461+
}
1462+
}
1463+
1464+
public struct AlwaysAddCodable: ExtensionMacro {
1465+
public static func expansion(
1466+
of node: AttributeSyntax,
1467+
attachedTo decl: some DeclGroupSyntax,
1468+
providingExtensionsOf type: some TypeSyntaxProtocol,
1469+
conformingTo protocols: [TypeSyntax],
1470+
in context: some MacroExpansionContext
1471+
) throws -> [ExtensionDeclSyntax] {
1472+
let decl: DeclSyntax =
1473+
"""
1474+
extension \(raw: type.trimmedDescription): Codable {
1475+
}
1476+
1477+
"""
1478+
1479+
return [
1480+
decl.cast(ExtensionDeclSyntax.self)
1481+
]
1482+
}
1483+
}
1484+
1485+
14301486
public struct ExtendableEnum: MemberMacro {
14311487
public static func expansion(
14321488
of node: AttributeSyntax,

test/Macros/accessor_macros.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,7 @@ struct MyBrokenStruct {
112112
}
113113
}
114114

115+
@myPropertyWrapper
116+
struct CannotHaveAccessors {}
117+
// CHECK-DIAGS: 'accessor' macro cannot be attached to struct
115118
#endif

0 commit comments

Comments
 (0)