Skip to content

Commit 9638062

Browse files
committed
[SourceKit] Implement macro expansion for (some) attached macros.
Extend the macro-expansion refactoring to work with member and member-attribute attached macros. These expansions can return several different changes, e.g., adding new members, sprinkling member attributes around, and so on.
1 parent 7ea0e3f commit 9638062

File tree

6 files changed

+221
-27
lines changed

6 files changed

+221
-27
lines changed

include/swift/AST/ASTWalker.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,17 @@ struct ReferenceMetaData {
4747
SemaReferenceKind Kind;
4848
llvm::Optional<AccessKind> AccKind;
4949
bool isImplicit = false;
50-
ReferenceMetaData(SemaReferenceKind Kind, llvm::Optional<AccessKind> AccKind,
51-
bool isImplicit = false)
52-
: Kind(Kind), AccKind(AccKind), isImplicit(isImplicit) {}
50+
51+
/// When non-none, this is a custom attribute reference.
52+
Optional<std::pair<const CustomAttr *, Decl *>> CustomAttrRef;
53+
54+
ReferenceMetaData(
55+
SemaReferenceKind Kind, llvm::Optional<AccessKind> AccKind,
56+
bool isImplicit = false,
57+
Optional<std::pair<const CustomAttr *, Decl *>> customAttrRef
58+
= None
59+
) : Kind(Kind), AccKind(AccKind), isImplicit(isImplicit),
60+
CustomAttrRef(customAttrRef) {}
5361
};
5462

5563
/// Specifies how the initialization expression of a \c lazy variable should be

include/swift/IDE/Utils.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ struct ResolvedCursorInfo {
151151
bool IsRef = true;
152152
Type Ty;
153153
Type ContainerType;
154+
Optional<std::pair<const CustomAttr *, Decl *>> CustomAttrRef = None;
155+
154156
bool IsKeywordArgument = false;
155157
/// It this is a ref, whether it is "dynamic". See \c ide::isDynamicRef.
156158
bool IsDynamic = false;
@@ -262,6 +264,13 @@ struct ResolvedValueRefCursorInfo : public ResolvedCursorInfo {
262264
: ValueRefInfo.ValueD;
263265
}
264266

267+
Optional<std::pair<const CustomAttr *, Decl *>> getCustomAttrRef() const {
268+
return ValueRefInfo.CustomAttrRef;
269+
}
270+
void setCustomAttrRef(Optional<std::pair<const CustomAttr *, Decl *>> ref) {
271+
ValueRefInfo.CustomAttrRef = ref;
272+
}
273+
265274
static bool classof(const ResolvedCursorInfo *Info) {
266275
return Info->getKind() == CursorInfoKind::ValueRef;
267276
}

lib/IDE/IDERequests.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ class CursorInfoResolver : public SourceEntityWalker {
9494
bool rangeContainsLoc(CharSourceRange Range) const;
9595
bool isDone() const { return CursorInfo.isValid(); }
9696
bool tryResolve(ValueDecl *D, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef,
97-
SourceLoc Loc, bool IsRef, Type Ty = Type());
97+
SourceLoc Loc, bool IsRef, Type Ty = Type(),
98+
Optional<ReferenceMetaData> Data = None);
9899
bool tryResolve(ModuleEntity Mod, SourceLoc Loc);
99100
bool tryResolve(Stmt *St);
100101
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
@@ -109,7 +110,8 @@ SourceManager &CursorInfoResolver::getSourceMgr() const
109110

110111
bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
111112
ExtensionDecl *ExtTyRef, SourceLoc Loc,
112-
bool IsRef, Type Ty) {
113+
bool IsRef, Type Ty,
114+
Optional<ReferenceMetaData> Data) {
113115
if (!D->hasName())
114116
return false;
115117

@@ -137,6 +139,9 @@ bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
137139
}
138140
}
139141

142+
if (Data)
143+
ValueRefInfo.setCustomAttrRef(Data->CustomAttrRef);
144+
140145
CursorInfo = ValueRefInfo;
141146
return true;
142147
}
@@ -260,7 +265,7 @@ bool CursorInfoResolver::visitDeclReference(ValueDecl *D,
260265
return false;
261266
if (Data.isImplicit || !Range.isValid())
262267
return true;
263-
return !tryResolve(D, CtorTyRef, ExtTyRef, Range.getStart(), /*IsRef=*/true, T);
268+
return !tryResolve(D, CtorTyRef, ExtTyRef, Range.getStart(), /*IsRef=*/true, T, Data);
264269
}
265270

266271
static bool isCursorOn(Expr *E, SourceLoc Loc) {

lib/IDE/SourceEntityWalker.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/ParameterList.h"
2020
#include "swift/AST/SourceFile.h"
2121
#include "swift/AST/Stmt.h"
22+
#include "swift/AST/TypeCheckRequests.h"
2223
#include "swift/AST/TypeRepr.h"
2324
#include "swift/AST/Types.h"
2425
#include "swift/Basic/Defer.h"
@@ -691,9 +692,23 @@ bool SemaAnnotator::handleCustomAttributes(Decl *D) {
691692
return true;
692693
}
693694
}
695+
694696
for (auto *customAttr : D->getAttrs().getAttributes<CustomAttr, true>()) {
695697
if (auto *Repr = customAttr->getTypeRepr()) {
696-
if (!Repr->walk(*this))
698+
// If this attribute resolves to a macro, index that.
699+
ASTContext &ctx = D->getASTContext();
700+
ResolveMacroRequest req{const_cast<CustomAttr *>(customAttr),
701+
getAttachedMacroRoles(),
702+
D->getInnermostDeclContext()};
703+
if (auto macroDecl = evaluateOrDefault(ctx.evaluator, req, nullptr)) {
704+
Type macroRefType = macroDecl->getDeclaredInterfaceType();
705+
if (!passReference(
706+
macroDecl, macroRefType, DeclNameLoc(Repr->getStartLoc()),
707+
ReferenceMetaData(SemaReferenceKind::DeclRef, None,
708+
/*isImplicit=*/false,
709+
std::make_pair(customAttr, D))))
710+
return false;
711+
} else if (!Repr->walk(*this))
697712
return false;
698713
}
699714
if (auto *SemaInit = customAttr->getSemanticInit()) {

lib/Refactoring/Refactoring.cpp

Lines changed: 139 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8470,21 +8470,91 @@ bool RefactoringActionAddAsyncWrapper::performChange() {
84708470
return false;
84718471
}
84728472

8473-
static MacroExpansionExpr *
8474-
findMacroExpansionTargetExpr(const ResolvedCursorInfo &Info) {
8473+
/// Retrieve the macro expansion buffer for the given macro expansion
8474+
/// expression.
8475+
static Optional<unsigned> getMacroExpansionBuffer(
8476+
SourceManager &sourceMgr, MacroExpansionExpr *expansion) {
8477+
if (auto rewritten = expansion->getRewritten()) {
8478+
return sourceMgr.findBufferContainingLoc(rewritten->getStartLoc());
8479+
}
8480+
8481+
return None;
8482+
}
8483+
8484+
/// Retrieve the macro expansion buffers for the given attached macro reference.
8485+
static llvm::SmallVector<unsigned, 2>
8486+
getMacroExpansionBuffers(MacroDecl *macro, const CustomAttr *attr, Decl *decl) {
8487+
auto roles = macro->getMacroRoles() & getAttachedMacroRoles();
8488+
if (!roles)
8489+
return { };
8490+
8491+
ASTContext &ctx = macro->getASTContext();
8492+
llvm::SmallVector<unsigned, 2> allBufferIDs;
8493+
if (roles.contains(MacroRole::Accessor)) {
8494+
// FIXME: Need to requestify.
8495+
}
8496+
8497+
if (roles.contains(MacroRole::MemberAttribute)) {
8498+
if (auto idc = dyn_cast<IterableDeclContext>(decl)) {
8499+
for (auto memberDecl : idc->getAllMembers()) {
8500+
auto bufferIDs = evaluateOrDefault(
8501+
ctx.evaluator, ExpandMemberAttributeMacros{memberDecl}, { });
8502+
allBufferIDs.append(bufferIDs.begin(), bufferIDs.end());
8503+
}
8504+
}
8505+
}
84758506

8507+
if (roles.contains(MacroRole::Member)) {
8508+
auto bufferIDs = evaluateOrDefault(
8509+
ctx.evaluator, ExpandSynthesizedMemberMacroRequest{decl}, { });
8510+
allBufferIDs.append(bufferIDs.begin(), bufferIDs.end());
8511+
}
8512+
8513+
// Drop any buffers that come from other macros. We could eliminate this
8514+
// step by adding more fine-grained requests above, which only expand for a
8515+
// single custom attribute.
8516+
SourceManager &sourceMgr = ctx.SourceMgr;
8517+
auto removedAt = std::remove_if(
8518+
allBufferIDs.begin(), allBufferIDs.end(),
8519+
[&](unsigned bufferID) {
8520+
auto generatedInfo = sourceMgr.getGeneratedSourceInfo(bufferID);
8521+
if (!generatedInfo)
8522+
return true;
8523+
8524+
return generatedInfo->attachedMacroCustomAttr != attr;
8525+
});
8526+
allBufferIDs.erase(removedAt, allBufferIDs.end());
8527+
return allBufferIDs;
8528+
}
8529+
8530+
/// Given a resolved cursor, determine whether it is for a macro expansion and
8531+
/// return the list of macro expansion buffer IDs that are associated with the
8532+
/// macro reference here.
8533+
static llvm::SmallVector<unsigned, 2>
8534+
getMacroExpansionBuffers(
8535+
SourceManager &sourceMgr, const ResolvedCursorInfo &Info
8536+
) {
84768537
// Handle '#' position in '#macroName(...)'.
84778538
if (auto exprInfo = dyn_cast<ResolvedExprStartCursorInfo>(&Info)) {
84788539
if (auto target =
8479-
dyn_cast_or_null<MacroExpansionExpr>(exprInfo->getTrailingExpr()))
8480-
if (target->getRewritten())
8481-
return target;
8482-
return nullptr;
8540+
dyn_cast_or_null<MacroExpansionExpr>(exprInfo->getTrailingExpr())) {
8541+
if (auto bufferID = getMacroExpansionBuffer(sourceMgr, target))
8542+
return { *bufferID };
8543+
}
8544+
8545+
return { };
84838546
}
84848547

8485-
// Handle 'macroName' position in '#macroName(...)'.
84868548
if (auto refInfo = dyn_cast<ResolvedValueRefCursorInfo>(&Info)) {
84878549
if (refInfo->isRef() && isa_and_nonnull<MacroDecl>(refInfo->getValueD())) {
8550+
// Handle 'macroName' position in '@macroName(...)'.
8551+
if (auto customAttrRef = refInfo->getCustomAttrRef()) {
8552+
auto macro = cast<MacroDecl>(refInfo->getValueD());
8553+
return getMacroExpansionBuffers(
8554+
macro, customAttrRef->first, customAttrRef->second);
8555+
}
8556+
8557+
// Handle 'macroName' position in '#macroName(...)'.
84888558
ContextFinder Finder(
84898559
*Info.getSourceFile(), Info.getLoc(), [&](ASTNode N) {
84908560
auto *expr =
@@ -8496,33 +8566,82 @@ findMacroExpansionTargetExpr(const ResolvedCursorInfo &Info) {
84968566
if (!Finder.getContexts().empty()) {
84978567
auto *target =
84988568
dyn_cast<MacroExpansionExpr>(Finder.getContexts()[0].get<Expr *>());
8499-
if (target->getRewritten())
8500-
return target;
8569+
if (target) {
8570+
if (auto bufferID = getMacroExpansionBuffer(sourceMgr, target))
8571+
return { *bufferID };
8572+
}
85018573
}
85028574
}
8503-
return nullptr;
8575+
8576+
return { };
85048577
}
85058578

85068579
// TODO: handle MacroExpansionDecl.
8507-
return nullptr;
8580+
return { };
85088581
}
85098582

85108583
bool RefactoringActionExpandMacro::isApplicable(const ResolvedCursorInfo &Info,
85118584
DiagnosticEngine &Diag) {
8512-
return findMacroExpansionTargetExpr(Info) != nullptr;
8585+
return !getMacroExpansionBuffers(Diag.SourceMgr, Info).empty();
85138586
}
85148587

85158588
bool RefactoringActionExpandMacro::performChange() {
8516-
auto target = findMacroExpansionTargetExpr(CursorInfo);
8517-
if (!target)
8589+
auto bufferIDs = getMacroExpansionBuffers(SM, CursorInfo);
8590+
if (bufferIDs.empty())
85188591
return true;
85198592

8520-
auto exprRange =
8521-
Lexer::getCharSourceRangeFromSourceRange(SM, target->getSourceRange());
8522-
auto rewrittenRange = Lexer::getCharSourceRangeFromSourceRange(
8523-
SM, target->getRewritten()->getSourceRange());
8524-
auto rewrittenBuffer = SM.extractText(rewrittenRange);
8525-
EditConsumer.accept(SM, exprRange, rewrittenBuffer);
8593+
// Send all of the rewritten buffer snippets.
8594+
CustomAttr *attachedMacroAttr = nullptr;
8595+
for (auto bufferID: bufferIDs) {
8596+
auto generatedInfo = SM.getGeneratedSourceInfo(bufferID);
8597+
if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid())
8598+
continue;
8599+
8600+
auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange);
8601+
8602+
// If there's no change, drop the edit entirely.
8603+
if (generatedInfo->originalSourceRange.getStart() ==
8604+
generatedInfo->originalSourceRange.getEnd() &&
8605+
rewrittenBuffer.empty())
8606+
continue;
8607+
8608+
auto originalSourceRange = generatedInfo->originalSourceRange;
8609+
8610+
// For member macros, adjust the source range from before-the-close-brace
8611+
// to after-the-open-brace.
8612+
if (generatedInfo->kind == GeneratedSourceInfo::MemberMacroExpansion) {
8613+
ASTNode node = ASTNode::getFromOpaqueValue(generatedInfo->astNode);
8614+
auto decl = node.dyn_cast<Decl *>();
8615+
if (!decl)
8616+
continue;
8617+
8618+
SourceLoc leftBraceLoc;
8619+
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
8620+
leftBraceLoc = nominal->getBraces().Start;
8621+
} else if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
8622+
leftBraceLoc = ext->getBraces().Start;
8623+
}
8624+
if (leftBraceLoc.isInvalid())
8625+
continue;
8626+
8627+
auto afterLeftBraceLoc = Lexer::getLocForEndOfToken(SM, leftBraceLoc);
8628+
originalSourceRange = CharSourceRange(afterLeftBraceLoc, 0);
8629+
}
8630+
8631+
EditConsumer.accept(SM, originalSourceRange, rewrittenBuffer);
8632+
8633+
if (generatedInfo->attachedMacroCustomAttr && !attachedMacroAttr)
8634+
attachedMacroAttr = generatedInfo->attachedMacroCustomAttr;
8635+
}
8636+
8637+
// For an attached macro, remove the custom attribute; it's been fully
8638+
// subsumed by its expansions.
8639+
if (attachedMacroAttr) {
8640+
SourceRange range = attachedMacroAttr->getRangeWithAt();
8641+
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
8642+
EditConsumer.accept(SM, charRange, StringRef());
8643+
}
8644+
85268645
return false;
85278646
}
85288647

test/SourceKit/Macros/macro_basic.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ func testStringify(a: Int, b: Int) {
44
_ = #stringify(a + b)
55
}
66

7+
@attached(memberAttribute)
8+
@attached(member)
9+
macro myTypeWrapper() = #externalMacro(module: "MacroDefinition", type: "TypeWrapperMacro")
10+
@attached(accessor) macro accessViaStorage() = #externalMacro(module: "MacroDefinition", type: "AccessViaStorageMacro")
11+
12+
struct _Storage {
13+
var x: Int = 0 {
14+
willSet { print("setting \(newValue)") }
15+
}
16+
var y: Int = 0 {
17+
willSet { print("setting \(newValue)") }
18+
}
19+
}
20+
21+
@myTypeWrapper
22+
struct S {
23+
var x: Int
24+
var y: Int
25+
}
26+
727
// FIXME: Swift parser is not enabled on Linux CI yet.
828
// REQUIRES: OS=macosx
929

@@ -54,3 +74,21 @@ func testStringify(a: Int, b: Int) {
5474
// EXPAND: source.edit.kind.active:
5575
// EXPAND-NEXT: 4:7-4:24 "(a + b, "a + b")"
5676

77+
//##-- cursor-info at 'macro name' position following @.
78+
// RUN: %sourcekitd-test -req=cursor -pos=21:2 -cursor-action %s -- ${COMPILER_ARGS[@]} | %FileCheck -check-prefix=CURSOR_ATTACHED_MACRO %s
79+
80+
// CURSOR_ATTACHED_MACRO-LABEL: ACTIONS BEGIN
81+
// CURSOR_ATTACHED_MACRO: source.refactoring.kind.expand.macro
82+
// CURSOR_ATTACHED_MACRO-NEXT: Expand Macro
83+
// CURSOR_ATTACHED_MACRO: ACTIONS END
84+
85+
//##-- Refactoring expanding the attached macro
86+
// RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=21:2 %s -- ${COMPILER_ARGS[@]} | %FileCheck -check-prefix=ATTACHED_EXPAND %s
87+
// ATTACHED_EXPAND: source.edit.kind.active:
88+
// ATTACHED_EXPAND: 23:3-23:3 "@accessViaStorage"
89+
// ATTACHED_EXPAND: source.edit.kind.active:
90+
// ATTACHED_EXPAND: 24:3-24:3 "@accessViaStorage"
91+
// ATTACHED_EXPAND: source.edit.kind.active:
92+
// ATTACHED_EXPAND: 22:11-22:11 "private var _storage = _Storage()"
93+
// ATTACHED_EXPAND: source.edit.kind.active:
94+
// ATTACHED_EXPAND: 21:1-21:15 ""

0 commit comments

Comments
 (0)