Skip to content

Commit 099d9cb

Browse files
authored
Merge pull request #66888 from rintaro/5.9-completion-macro-expanded-rdar110535113
[5.9][CodeCompletion] Suggest synthesized declarations from macros
2 parents 665a6c9 + 4d9c347 commit 099d9cb

File tree

7 files changed

+188
-39
lines changed

7 files changed

+188
-39
lines changed

lib/AST/Decl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3815,6 +3815,10 @@ bool ValueDecl::shouldHideFromEditor() const {
38153815
getBaseIdentifier().str().startswith("$__"))
38163816
return true;
38173817

3818+
// Macro unique names are only intended to be used inside the expanded code.
3819+
if (MacroDecl::isUniqueMacroName(getBaseName()))
3820+
return true;
3821+
38183822
return false;
38193823
}
38203824

lib/AST/Module.cpp

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,6 @@ class swift::SourceLookupCache {
188188
SourceLookupCache(const SourceFile &SF);
189189
SourceLookupCache(const ModuleDecl &Mod);
190190

191-
/// Throw away as much memory as possible.
192-
void invalidate();
193-
194191
void lookupValue(DeclName Name, NLKind LookupKind,
195192
OptionSet<ModuleLookupFlags> Flags,
196193
SmallVectorImpl<ValueDecl*> &Result);
@@ -552,6 +549,29 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath,
552549
Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel);
553550
}
554551
}
552+
553+
populateAuxiliaryDeclCache();
554+
SmallVector<MissingDecl *, 4> unexpandedDecls;
555+
for (auto &entry : TopLevelAuxiliaryDecls) {
556+
for (auto &decl : entry.second) {
557+
unexpandedDecls.append(entry.second.begin(), entry.second.end());
558+
}
559+
}
560+
561+
// Store macro expanded decls in a 'SmallSetVector' because different
562+
// MissingDecls might be created by a single macro expansion. (e.g. multiple
563+
// 'names' in macro role attributes). Since expansions are cached, it doesn't
564+
// cause duplicated expansions, but different 'unexpandedDecl' may report the
565+
// same 'ValueDecl'.
566+
SmallSetVector<ValueDecl *, 4> macroExpandedDecls;
567+
for (MissingDecl *unexpandedDecl : unexpandedDecls) {
568+
unexpandedDecl->forEachMacroExpandedDecl([&](ValueDecl *vd) {
569+
macroExpandedDecls.insert(vd);
570+
});
571+
}
572+
for (auto *vd : macroExpandedDecls) {
573+
Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel);
574+
}
555575
}
556576

557577
void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath,
@@ -608,16 +628,6 @@ void SourceLookupCache::lookupClassMember(ImportPath::Access accessPath,
608628
results.append(iter->second.begin(), iter->second.end());
609629
}
610630

611-
void SourceLookupCache::invalidate() {
612-
TopLevelValues.clear();
613-
ClassMembers.clear();
614-
MemberCachePopulated = false;
615-
616-
// std::move AllVisibleValues into a temporary to destroy its contents.
617-
using SameSizeSmallVector = decltype(AllVisibleValues);
618-
(void)SameSizeSmallVector{std::move(AllVisibleValues)};
619-
}
620-
621631
//===----------------------------------------------------------------------===//
622632
// Module Implementation
623633
//===----------------------------------------------------------------------===//
@@ -1067,9 +1077,12 @@ void SourceFile::lookupValue(DeclName name, NLKind lookupKind,
10671077
void ModuleDecl::lookupVisibleDecls(ImportPath::Access AccessPath,
10681078
VisibleDeclConsumer &Consumer,
10691079
NLKind LookupKind) const {
1070-
if (isParsedModule(this))
1071-
return getSourceLookupCache().lookupVisibleDecls(
1072-
AccessPath, Consumer, LookupKind);
1080+
if (isParsedModule(this)) {
1081+
auto &cache = getSourceLookupCache();
1082+
cache.lookupVisibleDecls(AccessPath, Consumer, LookupKind);
1083+
assert(Cache.get() == &cache && "cache invalidated during lookup");
1084+
return;
1085+
}
10731086

10741087
FORWARD(lookupVisibleDecls, (AccessPath, Consumer, LookupKind));
10751088
}
@@ -3512,7 +3525,6 @@ void ModuleDecl::clearLookupCache() {
35123525
return;
35133526

35143527
// Abandon any current cache. We'll rebuild it on demand.
3515-
Cache->invalidate();
35163528
Cache.reset();
35173529
}
35183530

@@ -3932,9 +3944,6 @@ SynthesizedFileUnit &FileUnit::getOrCreateSynthesizedFile() {
39323944
return *thisSynth;
39333945
SynthesizedFile = new (getASTContext()) SynthesizedFileUnit(*this);
39343946
SynthesizedFileAndKind.setPointer(SynthesizedFile);
3935-
// Rebuild the source lookup caches now that we have a synthesized file
3936-
// full of declarations to look into.
3937-
getParentModule()->clearLookupCache();
39383947
}
39393948
return *SynthesizedFile;
39403949
}

lib/AST/NameLookup.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ void UsableFilteringDeclConsumer::foundDecl(ValueDecl *D,
172172
if (auto *contextD = DC->getAsDecl())
173173
tmpLoc = contextD->getStartLoc();
174174
}
175-
if (!SM.isBeforeInBuffer(D->getLoc(), Loc))
175+
auto declLoc = DC->getParentModule()->getOriginalLocation(D->getLoc()).second;
176+
if (!SM.isBeforeInBuffer(declLoc, tmpLoc))
176177
return;
177178
}
178179

@@ -3850,20 +3851,48 @@ void FindLocalVal::visitBraceStmt(BraceStmt *S, bool isTopLevelCode) {
38503851
return;
38513852
}
38523853

3854+
// Visit inner statements first before reporting local decls in the current
3855+
// scope.
38533856
for (auto elem : S->getElements()) {
38543857
// If we have a SingleValueStmtExpr, there may be local bindings in the
38553858
// wrapped statement.
38563859
if (auto *E = elem.dyn_cast<Expr *>()) {
38573860
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E))
38583861
visit(SVE->getStmt());
3862+
continue;
38593863
}
3860-
if (auto *S = elem.dyn_cast<Stmt*>())
3864+
3865+
if (auto *S = elem.dyn_cast<Stmt*>()) {
38613866
visit(S);
3867+
continue;
3868+
}
38623869
}
3863-
for (auto elem : S->getElements()) {
3864-
if (auto *D = elem.dyn_cast<Decl*>()) {
3870+
3871+
auto visitDecl = [&](Decl *D) {
3872+
if (auto *VD = dyn_cast<ValueDecl>(D))
3873+
checkValueDecl(VD, DeclVisibilityKind::LocalVariable);
3874+
D->visitAuxiliaryDecls([&](Decl *D) {
38653875
if (auto *VD = dyn_cast<ValueDecl>(D))
38663876
checkValueDecl(VD, DeclVisibilityKind::LocalVariable);
3877+
// FIXME: Recursively call `visitDecl` to handle nested macros.
3878+
});
3879+
};
3880+
3881+
for (auto elem : S->getElements()) {
3882+
if (auto *E = elem.dyn_cast<Expr *>()) {
3883+
// 'MacroExpansionExpr' at code-item position may introduce value decls.
3884+
// NOTE: the expression must be type checked.
3885+
// FIXME: In code-completion local expressions are _not_ type checked.
3886+
if (auto *mee = dyn_cast<MacroExpansionExpr>(E)) {
3887+
if (auto *med = mee->getSubstituteDecl()) {
3888+
visitDecl(med);
3889+
}
3890+
}
3891+
continue;
3892+
}
3893+
if (auto *D = elem.dyn_cast<Decl*>()) {
3894+
visitDecl(D);
3895+
continue;
38673896
}
38683897
}
38693898
}

lib/SILOptimizer/Differentiation/LinearMapInfo.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ LinearMapInfo::createBranchingTraceDecl(SILBasicBlock *originalBB,
117117
branchingTraceDecl->setAccess(AccessLevel::Internal);
118118
}
119119
file.addTopLevelDecl(branchingTraceDecl);
120+
file.getParentModule()->clearLookupCache();
120121

121122
return branchingTraceDecl;
122123
}

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -217,17 +217,24 @@ static void collectVisibleMemberDecls(const DeclContext *CurrDC, LookupState LS,
217217
Type BaseType,
218218
IterableDeclContext *Parent,
219219
SmallVectorImpl<ValueDecl *> &FoundDecls) {
220-
for (auto Member : Parent->getMembers()) {
221-
auto *VD = dyn_cast<ValueDecl>(Member);
220+
auto check = [&](Decl *decl) {
221+
auto *VD = dyn_cast<ValueDecl>(decl);
222222
if (!VD)
223-
continue;
223+
return;
224224
if (!isDeclVisibleInLookupMode(VD, LS, CurrDC))
225-
continue;
225+
return;
226226
if (!evaluateOrDefault(CurrDC->getASTContext().evaluator,
227227
IsDeclApplicableRequest(DeclApplicabilityOwner(CurrDC, BaseType, VD)),
228228
false))
229-
continue;
229+
return;
230230
FoundDecls.push_back(VD);
231+
};
232+
233+
for (auto Member : Parent->getMembers()) {
234+
check(Member);
235+
Member->visitAuxiliaryDecls([&](Decl *d) {
236+
check(d);
237+
});
231238
}
232239
}
233240

@@ -634,16 +641,6 @@ static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD,
634641
false);
635642
}
636643

637-
// Expand peer macros.
638-
for (auto *member : NTD->getMembers()) {
639-
if (!ctx.evaluator.hasActiveRequest(ExpandPeerMacroRequest{member})) {
640-
(void)evaluateOrDefault(
641-
ctx.evaluator,
642-
ExpandPeerMacroRequest{member},
643-
{});
644-
}
645-
}
646-
647644
synthesizePropertyWrapperVariables(NTD);
648645
}
649646

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: mkdir -p %t/plugins
5+
6+
//##-- Prepare the macro plugin.
7+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/plugins/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/../Macros/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
8+
9+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -plugin-path %t/plugins -parse-as-library
10+
11+
@freestanding(declaration, names: named(A), named(B), named(foo), named(addOne))
12+
macro defineDeclsWithKnownNames() = #externalMacro(module: "MacroDefinition", type: "DefineDeclsWithKnownNamesMacro")
13+
14+
@attached(peer, names: suffixed(_peer))
15+
macro PeerWithSuffix() = #externalMacro(module: "MacroDefinition", type: "PeerValueWithSuffixNameMacro")
16+
17+
@attached(peer, names: arbitrary)
18+
macro PeerWithSuffixAsArbitrary() = #externalMacro(module: "MacroDefinition", type: "PeerValueWithSuffixNameMacro")
19+
20+
@freestanding(declaration, names: arbitrary)
21+
macro VarValueDecl() = #externalMacro(module: "MacroDefinition", type: "VarValueMacro")
22+
23+
24+
#defineDeclsWithKnownNames
25+
26+
@PeerWithSuffix
27+
func globalFunc() {}
28+
29+
func test() {
30+
#^GLOBAL^#
31+
// GLOBAL-DAG: Decl[Struct]/CurrModule: A[#A#]; name=A
32+
// GLOBAL-DAG: Decl[Struct]/CurrModule: B[#B#]; name=B
33+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: foo[#Int#]; name=foo
34+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: addOne[#(Int) -> Int#]; name=addOne
35+
// GLOBAL-DAG: Decl[FreeFunction]/CurrModule: globalFunc()[#Void#]; name=globalFunc()
36+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: globalFunc_peer[#Int#]; name=globalFunc_peer
37+
}
38+
39+
struct MyStruct {
40+
@PeerWithSuffix
41+
func instanceMethod() {}
42+
43+
@PeerWithSuffix
44+
static func staticMethod() {}
45+
46+
@PeerWithSuffixAsArbitrary
47+
func forArbitrary() {}
48+
49+
#defineDeclsWithKnownNames
50+
51+
#VarValueDecl
52+
}
53+
54+
func testMemberInstance(value: MyStruct) {
55+
value.#^MEMBER_INSTANCE^#
56+
// MEMBER_INSTANCE-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self
57+
// MEMBER_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod()[#Void#]; name=instanceMethod()
58+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: instanceMethod_peer[#Int#]; name=instanceMethod_peer
59+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: staticMethod_peer[#Int#]; name=staticMethod_peer
60+
// MEMBER_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: forArbitrary()[#Void#]; name=forArbitrary()
61+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: forArbitrary_peer[#Int#]; name=forArbitrary_peer
62+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
63+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: addOne[#(Int) -> Int#]; name=addOne
64+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: value[#Int#]; name=value
65+
// NOTE: 'staticMethod_peer' is a instance var because the macro emits the decl without 'static'
66+
}
67+
68+
func testMemberStatic() {
69+
MyStruct.#^MEMBER_STATIC^#
70+
// MEMBER_STATIC-NOT: _peer
71+
// MEMBER_STATIC-DAG: Keyword[self]/CurrNominal: self[#MyStruct.Type#]; name=self
72+
// MEMBER_STATIC-DAG: Keyword/CurrNominal: Type[#MyStruct.Type#]; name=Type
73+
// MEMBER_STATIC-DAG: Decl[Struct]/CurrNominal: A[#MyStruct.A#]; name=A
74+
// MEMBER_STATIC-DAG: Decl[Struct]/CurrNominal: B[#MyStruct.B#]; name=B
75+
// MEMBER_STATIC-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod({#(self): MyStruct#})[#() -> Void#]; name=instanceMethod(:)
76+
// MEMBER_STATIC-DAG: Decl[StaticMethod]/CurrNominal: staticMethod()[#Void#]; name=staticMethod()
77+
// MEMBER_STATIC-NOT: _peer
78+
}
79+
80+
func testLocal() {
81+
#defineDeclsWithKnownNames
82+
83+
@PeerWithSuffix func localFunc() {}
84+
85+
do {
86+
#^LOCAL?skip=FIXME^#
87+
// FIXME: macros in replace function bodies are not handled correclty.
88+
// FIXME: decls instroduced by #defineDeclsWithKnownNames are missing.
89+
// LOCAL-DAG: Decl[FreeFunction]/Local: localFunc()[#Void#]; name=localFunc()
90+
// LOCAL-DAG: Decl[LocalVar]/Local: localFunc_peer[#Int#]; name=localFunc_peer
91+
// LOCAL-DAG: Decl[Struct]/Local: A[#A#]; name=A
92+
// LOCAL-DAG: Decl[Struct]/Local: B[#B#]; name=B
93+
// LOCAL-DAG: Decl[LocalVar]/Local: foo[#Int#]; name=foo
94+
// LOCAL-DAG: Decl[LocalVar]/Local: addOne[#(Int) -> Int#]; name=addOne
95+
}
96+
}

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,3 +1641,16 @@ public struct InitializableMacro: ConformanceMacro, MemberMacro {
16411641
return [requirement]
16421642
}
16431643
}
1644+
1645+
public struct PeerValueWithSuffixNameMacro: PeerMacro {
1646+
public static func expansion(
1647+
of node: AttributeSyntax,
1648+
providingPeersOf declaration: some DeclSyntaxProtocol,
1649+
in context: some MacroExpansionContext
1650+
) throws -> [DeclSyntax] {
1651+
guard let identified = declaration.asProtocol(IdentifiedDeclSyntax.self) else {
1652+
throw CustomError.message("Macro can only be applied to an identified declarations.")
1653+
}
1654+
return ["var \(raw: identified.identifier.text)_peer: Int { 1 }"]
1655+
}
1656+
}

0 commit comments

Comments
 (0)