Skip to content

Commit f88f181

Browse files
committed
[CodeCompletion] Suggest synthesized declarations from macros
* Don't invalidate the lookup cache in 'getOrCreateSynthesizedFile()' Adding a synthesized file itself doesn't introduce any decls. Instead, we should invalidate the right after the actual declrations are added in the file * Remove 'SourceLookupCache::invalidate()' method. It was just used right before the destruction. It was just not necessary * Include auxiliary decls in 'SourceLookupCache::lookupVisibleDecls()' Previously, global symbol completion didn't include decls synthesized by peer macros or freestanding decl macros * Include "auxiliary decls" in visible member lookup, and visible local decl lookup * Hide macro unique names rdar://110535113
1 parent 262e238 commit f88f181

File tree

7 files changed

+167
-38
lines changed

7 files changed

+167
-38
lines changed

lib/AST/Decl.cpp

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

3830+
// Macro unique names are only intended to be used inside the expanded code.
3831+
if (MacroDecl::isUniqueMacroName(getBaseName()))
3832+
return true;
3833+
38303834
return false;
38313835
}
38323836

lib/AST/Module.cpp

Lines changed: 31 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,31 @@ 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+
unexpandedDecls.append(TopLevelArbitraryMacros.begin(),
561+
TopLevelArbitraryMacros.end());
562+
563+
// Store macro expanded decls in a 'SmallSetVector' because different
564+
// MissingDecls might be created by a single macro expansion. (e.g. multiple
565+
// 'names' in macro role attributes). Since expansions are cached, it doesn't
566+
// cause duplicated expansions, but different 'unexpandedDecl' may report the
567+
// same 'ValueDecl'.
568+
SmallSetVector<ValueDecl *, 4> macroExpandedDecls;
569+
for (MissingDecl *unexpandedDecl : unexpandedDecls) {
570+
unexpandedDecl->forEachMacroExpandedDecl([&](ValueDecl *vd) {
571+
macroExpandedDecls.insert(vd);
572+
});
573+
}
574+
for (auto *vd : macroExpandedDecls) {
575+
Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel);
576+
}
555577
}
556578

557579
void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath,
@@ -608,16 +630,6 @@ void SourceLookupCache::lookupClassMember(ImportPath::Access accessPath,
608630
results.append(iter->second.begin(), iter->second.end());
609631
}
610632

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-
621633
//===----------------------------------------------------------------------===//
622634
// Module Implementation
623635
//===----------------------------------------------------------------------===//
@@ -1067,9 +1079,12 @@ void SourceFile::lookupValue(DeclName name, NLKind lookupKind,
10671079
void ModuleDecl::lookupVisibleDecls(ImportPath::Access AccessPath,
10681080
VisibleDeclConsumer &Consumer,
10691081
NLKind LookupKind) const {
1070-
if (isParsedModule(this))
1071-
return getSourceLookupCache().lookupVisibleDecls(
1072-
AccessPath, Consumer, LookupKind);
1082+
if (isParsedModule(this)) {
1083+
auto &cache = getSourceLookupCache();
1084+
cache.lookupVisibleDecls(AccessPath, Consumer, LookupKind);
1085+
assert(Cache.get() == &cache && "cache invalidated during lookup");
1086+
return;
1087+
}
10731088

10741089
FORWARD(lookupVisibleDecls, (AccessPath, Consumer, LookupKind));
10751090
}
@@ -3524,7 +3539,6 @@ void ModuleDecl::clearLookupCache() {
35243539
return;
35253540

35263541
// Abandon any current cache. We'll rebuild it on demand.
3527-
Cache->invalidate();
35283542
Cache.reset();
35293543
}
35303544

@@ -3944,9 +3958,6 @@ SynthesizedFileUnit &FileUnit::getOrCreateSynthesizedFile() {
39443958
return *thisSynth;
39453959
SynthesizedFile = new (getASTContext()) SynthesizedFileUnit(*this);
39463960
SynthesizedFileAndKind.setPointer(SynthesizedFile);
3947-
// Rebuild the source lookup caches now that we have a synthesized file
3948-
// full of declarations to look into.
3949-
getParentModule()->clearLookupCache();
39503961
}
39513962
return *SynthesizedFile;
39523963
}

lib/AST/NameLookup.cpp

Lines changed: 29 additions & 3 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

@@ -3875,20 +3876,45 @@ void FindLocalVal::visitBraceStmt(BraceStmt *S, bool isTopLevelCode) {
38753876
return;
38763877
}
38773878

3879+
// Visit inner statements first before reporting local decls in the current
3880+
// scope.
38783881
for (auto elem : S->getElements()) {
38793882
// If we have a SingleValueStmtExpr, there may be local bindings in the
38803883
// wrapped statement.
38813884
if (auto *E = elem.dyn_cast<Expr *>()) {
38823885
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E))
38833886
visit(SVE->getStmt());
3887+
continue;
38843888
}
3889+
38853890
if (auto *S = elem.dyn_cast<Stmt*>())
38863891
visit(S);
38873892
}
3888-
for (auto elem : S->getElements()) {
3889-
if (auto *D = elem.dyn_cast<Decl*>()) {
3893+
3894+
auto visitDecl = [&](Decl *D) {
3895+
if (auto *VD = dyn_cast<ValueDecl>(D))
3896+
checkValueDecl(VD, DeclVisibilityKind::LocalVariable);
3897+
D->visitAuxiliaryDecls([&](Decl *D) {
38903898
if (auto *VD = dyn_cast<ValueDecl>(D))
38913899
checkValueDecl(VD, DeclVisibilityKind::LocalVariable);
3900+
// FIXME: Recursively call `visitDecl` to handle nested macros.
3901+
});
3902+
};
3903+
3904+
for (auto elem : S->getElements()) {
3905+
if (auto *E = elem.dyn_cast<Expr *>()) {
3906+
// 'MacroExpansionExpr' at code-item position may introduce value decls.
3907+
// NOTE: the expression must be type checked.
3908+
// FIXME: In code-completion local expressions are _not_ type checked.
3909+
if (auto *mee = dyn_cast<MacroExpansionExpr>(E)) {
3910+
if (auto *med = mee->getSubstituteDecl()) {
3911+
visitDecl(med);
3912+
}
3913+
}
3914+
}
3915+
if (auto *D = elem.dyn_cast<Decl*>()) {
3916+
visitDecl(D);
3917+
continue;
38923918
}
38933919
}
38943920
}

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: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: mkdir -p %t/plugins
3+
4+
//##-- Prepare the macro plugin.
5+
// 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
6+
7+
// 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
8+
9+
@freestanding(declaration, names: named(A), named(B), named(foo), named(addOne))
10+
macro defineDeclsWithKnownNames() = #externalMacro(module: "MacroDefinition", type: "DefineDeclsWithKnownNamesMacro")
11+
12+
@attached(peer, names: suffixed(_peer))
13+
macro PeerWithSuffix() = #externalMacro(module: "MacroDefinition", type: "PeerValueWithSuffixNameMacro")
14+
15+
#defineDeclsWithKnownNames
16+
17+
@PeerWithSuffix
18+
func globalFunc() {}
19+
20+
func test() {
21+
#^GLOBAL^#
22+
// GLOBAL-DAG: Decl[Struct]/CurrModule: A[#A#]; name=A
23+
// GLOBAL-DAG: Decl[Struct]/CurrModule: B[#B#]; name=B
24+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: foo[#Int#]; name=foo
25+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: addOne[#(Int) -> Int#]; name=addOne
26+
// GLOBAL-DAG: Decl[FreeFunction]/CurrModule: globalFunc()[#Void#]; name=globalFunc()
27+
// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: globalFunc_peer[#Int#]; name=globalFunc_peer
28+
}
29+
30+
struct MyStruct {
31+
@PeerWithSuffix
32+
func instanceMethod() {}
33+
34+
@PeerWithSuffix
35+
static func staticMethod() {}
36+
37+
#defineDeclsWithKnownNames
38+
}
39+
40+
func testMember(value: MyStruct) {
41+
value.#^MEMBER_INSTANCE^#
42+
// MEMBER_INSTANCE-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self
43+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
44+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: addOne[#(Int) -> Int#]; name=addOne
45+
// MEMBER_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod()[#Void#]; name=instanceMethod()
46+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: instanceMethod_peer[#Int#]; name=instanceMethod_peer
47+
// MEMBER_INSTANCE-DAG: Decl[InstanceVar]/CurrNominal: staticMethod_peer[#Int#]; name=staticMethod_peer
48+
// NOTE: 'staticMethod_peer' is a instance var because the macro emits the decl without 'static'
49+
50+
MyStruct.#^MEMBER_STATIC^#
51+
// MEMBER_STATIC-NOT: _peer
52+
// MEMBER_STATIC-DAG: Keyword[self]/CurrNominal: self[#MyStruct.Type#]; name=self
53+
// MEMBER_STATIC-DAG: Keyword/CurrNominal: Type[#MyStruct.Type#]; name=Type
54+
// MEMBER_STATIC-DAG: Decl[Struct]/CurrNominal: A[#MyStruct.A#]; name=A
55+
// MEMBER_STATIC-DAG: Decl[Struct]/CurrNominal: B[#MyStruct.B#]; name=B
56+
// MEMBER_STATIC-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod({#(self): MyStruct#})[#() -> Void#]; name=instanceMethod(:)
57+
// MEMBER_STATIC-DAG: Decl[StaticMethod]/CurrNominal: staticMethod()[#Void#]; name=staticMethod()
58+
// MEMBER_STATIC-NOT: _peer
59+
}
60+
61+
func testLocal() {
62+
#defineDeclsWithKnownNames
63+
64+
@PeerWithSuffix func localFunc() {}
65+
66+
do {
67+
#^LOCAL?skip=FIXME^#
68+
// FIXME: macros in replace function bodies are not handled correclty.
69+
// FIXME: decls instroduced by #defineDeclsWithKnownNames are missing.
70+
// LOCAL-DAG: Decl[FreeFunction]/Local: localFunc()[#Void#]; name=localFunc()
71+
// LOCAL-DAG: Decl[LocalVar]/Local: localFunc_peer[#Int#]; name=localFunc_peer
72+
// LOCAL-DAG: Decl[Struct]/Local: A[#A#]; name=A
73+
// LOCAL-DAG: Decl[Struct]/Local: B[#B#]; name=B
74+
// LOCAL-DAG: Decl[LocalVar]/Local: foo[#Int#]; name=foo
75+
// LOCAL-DAG: Decl[LocalVar]/Local: addOne[#(Int) -> Int#]; name=addOne
76+
}
77+
}

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,3 +1626,16 @@ public struct InitializableMacro: ConformanceMacro, MemberMacro {
16261626
return [requirement]
16271627
}
16281628
}
1629+
1630+
public struct PeerValueWithSuffixNameMacro: PeerMacro {
1631+
public static func expansion(
1632+
of node: AttributeSyntax,
1633+
providingPeersOf declaration: some DeclSyntaxProtocol,
1634+
in context: some MacroExpansionContext
1635+
) throws -> [DeclSyntax] {
1636+
guard let identified = declaration.asProtocol(IdentifiedDeclSyntax.self) else {
1637+
throw CustomError.message("Macro can only be applied to an identified declarations.")
1638+
}
1639+
return ["var \(raw: identified.identifier.text)_peer: Int { 1 }"]
1640+
}
1641+
}

0 commit comments

Comments
 (0)