Skip to content

[Macros] Walk semantic attributes when checking a primary file #63371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1498,7 +1498,7 @@ void TypeChecker::checkDeclAttributes(Decl *D) {
llvm::SmallVector<AvailableAttr *, 4> availableAttrs;
llvm::SmallVector<BackDeployAttr *, 4> backDeployAttrs;
llvm::SmallVector<OriginallyDefinedInAttr*, 4> ODIAttrs;
for (auto attr : D->getAttrs()) {
for (auto attr : D->getSemanticAttrs()) {
if (!attr->isValid()) continue;

// If Attr.def says that the attribute cannot appear on this kind of
Expand Down
7 changes: 1 addition & 6 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1992,12 +1992,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
checkAccessControl(PGD);
}

void visitMissingDecl(MissingDecl *missing) {
// FIXME: Expanded attribute lists should be type checked against
// the real declaration they will be attached to. Attempting to
// type check a missing decl should produce an error.
TypeChecker::checkDeclAttributes(missing);
}
void visitMissingDecl(MissingDecl *missing) { }

void visitMissingMemberDecl(MissingMemberDecl *MMD) {
llvm_unreachable("should always be type-checked already");
Expand Down
29 changes: 21 additions & 8 deletions lib/Sema/TypeCheckMacros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ bool ExpandMemberAttributeMacros::evaluate(Evaluator &evaluator,
if (!parentDecl)
return false;

if (isa<PatternBindingDecl>(decl))
return false;

bool addedAttributes = false;
parentDecl->forEachAttachedMacro(MacroRole::MemberAttribute,
[&](CustomAttr *attr, MacroDecl *macro) {
Expand Down Expand Up @@ -882,7 +885,24 @@ void swift::expandAccessors(
// Trigger parsing of the sequence of accessor declarations. This has the
// side effect of registering those accessor declarations with the storage
// declaration, so there is nothing further to do.
(void)macroSourceFile->getTopLevelItems();
for (auto decl : macroSourceFile->getTopLevelItems()) {
auto accessor = dyn_cast_or_null<AccessorDecl>(decl.dyn_cast<Decl *>());
if (!accessor)
continue;

if (accessor->isObservingAccessor())
continue;

// If any non-observing accessor was added, remove the initializer if
// there is one.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (auto binding = var->getParentPatternBinding()) {
unsigned index = binding->getPatternEntryIndexForVarDecl(var);
binding->setInit(index, nullptr);
break;
}
}
}
}

// FIXME: Almost entirely duplicated code from `expandAccessors`.
Expand Down Expand Up @@ -1065,13 +1085,6 @@ bool swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) {
bool addedAttributes = false;
auto topLevelDecls = macroSourceFile->getTopLevelDecls();
for (auto decl : topLevelDecls) {
// FIXME: We want to type check decl attributes applied to
// the real declaration, ideally by appending the new attributes
// to the result and changing TypeChecker::checkDeclAttributes
// to use the semantic attribute list.
decl->setDeclContext(dc);
TypeChecker::typeCheckDecl(decl);

// Add the new attributes to the semantic attribute list.
SmallVector<DeclAttribute *, 2> attrs(decl->getAttrs().begin(),
decl->getAttrs().end());
Expand Down
4 changes: 2 additions & 2 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2657,10 +2657,10 @@ getActualMacroRole(uint8_t context) {
CASE(Declaration)
CASE(Accessor)
CASE(MemberAttribute)
CASE(Member)
#undef CASE
default:
return None;
}
return None;
}

static Optional<swift::MacroIntroducedDeclNameKind>
Expand Down
94 changes: 94 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -439,3 +439,97 @@ public struct AddMembers: MemberMacro {
]
}
}

/// Implementation of the `wrapStoredProperties` macro, which can be
/// used to apply an attribute to all of the stored properties of a type.
///
/// This macro demonstrates member-attribute macros.
public struct WrapStoredPropertiesMacro: MemberAttributeMacro {
public static func expansion<
Declaration: DeclGroupSyntax,
Context: MacroExpansionContext
>(
of node: AttributeSyntax,
attachedTo decl: Declaration,
providingAttributesFor member: DeclSyntax,
in context: Context
) throws -> [AttributeSyntax] {
guard let property = member.as(VariableDeclSyntax.self),
property.isStoredProperty
else {
return []
}

guard case let .argumentList(arguments) = node.argument,
let firstElement = arguments.first,
let stringLiteral = firstElement.expression
.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(wrapperName)? = stringLiteral.segments.first else {
throw CustomError.message("macro requires a string literal containing the name of an attribute")
}

return [
AttributeSyntax(
attributeName: SimpleTypeIdentifierSyntax(
name: .identifier(wrapperName.content.text)
)
)
.with(\.leadingTrivia, [.newlines(1), .spaces(2)])
]
}
}

extension VariableDeclSyntax {
/// Determine whether this variable has the syntax of a stored property.
///
/// This syntactic check cannot account for semantic adjustments due to,
/// e.g., accessor macros or property wrappers.
var isStoredProperty: Bool {
if bindings.count != 1 {
return false
}

let binding = bindings.first!
switch binding.accessor {
case .none:
return true

case .accessors(let node):
for accessor in node.accessors {
switch accessor.accessorKind.tokenKind {
case .keyword(.willSet), .keyword(.didSet):
// Observers can occur on a stored property.
break

default:
// Other accessors make it a computed property.
return false
}
}

return true

case .getter:
return false

@unknown default:
return false
}
}
}

extension DeclGroupSyntax {
/// Enumerate the stored properties that syntactically occur in this
/// declaration.
func storedProperties() -> [VariableDeclSyntax] {
return members.members.compactMap { member in
guard let variable = member.decl.as(VariableDeclSyntax.self),
variable.isStoredProperty else {
return nil
}

return variable
}
}
}
4 changes: 2 additions & 2 deletions test/Macros/accessor_macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %target-build-swift -I %swift-host-lib-dir -L %swift-host-lib-dir -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath

// First check for no errors.
// RUN: %target-typecheck-verify-swift -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s
// RUN: %target-typecheck-verify-swift -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir

// Check that the expansion buffer are as expected.
// RUN: %target-swift-frontend -typecheck -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s -dump-macro-expansions > %t/expansions-dump.txt 2>&1
Expand Down Expand Up @@ -53,7 +53,7 @@ struct MyStruct {
// CHECK-DUMP: }

@myPropertyWrapper
var birthDate: Date?
var birthDate: Date? = nil
// CHECK-DUMP: macro:birthDate@myPropertyWrapper
// CHECK-DUMP: get {
// CHECK-DUMP: _birthDate.wrappedValue
Expand Down
14 changes: 14 additions & 0 deletions test/Macros/macro_expand_attributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

@attached(memberAttribute) macro wrapAllProperties() = #externalMacro(module: "MacroDefinition", type: "WrapAllProperties")

@attached(memberAttribute)
public macro wrapStoredProperties(_ attributeName: String) = #externalMacro(module: "MacroDefinition", type: "WrapStoredPropertiesMacro")

@propertyWrapper
struct Wrapper<T> {
init(wrappedValue: T) {
Expand Down Expand Up @@ -99,3 +102,14 @@ _ = c.y
c.x = 10
// CHECK: writing to key-path
c.y = 100

// Use the "wrapStoredProperties" macro to deprecate all of the stored
// properties.
@wrapStoredProperties(#"available(*, deprecated, message: "hands off my data")"#)
struct OldStorage {
var x: Int
}

// The deprecation warning below comes from the deprecation attribute
// introduced by @wrapStoredProperties on OldStorage.
_ = OldStorage(x: 5).x // expected-warning{{'x' is deprecated: hands off my data}}