Skip to content

Commit 96c7e81

Browse files
authored
Merge pull request #58530 from rintaro/5.7-ide-completion-shadowing-rdar86285396
[5.7][LookupVisibleDecls] Implement shadowing for unqualified lookups
2 parents db6de66 + 1f56100 commit 96c7e81

15 files changed

+506
-76
lines changed

include/swift/AST/NameLookup.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,13 +420,17 @@ class AccessFilteringDeclConsumer final : public VisibleDeclConsumer {
420420
class UsableFilteringDeclConsumer final : public VisibleDeclConsumer {
421421
const SourceManager &SM;
422422
const DeclContext *DC;
423+
const DeclContext *typeContext;
423424
SourceLoc Loc;
425+
llvm::DenseMap<DeclBaseName, std::pair<ValueDecl *, DeclVisibilityKind>>
426+
SeenNames;
424427
VisibleDeclConsumer &ChainedConsumer;
425428

426429
public:
427430
UsableFilteringDeclConsumer(const SourceManager &SM, const DeclContext *DC,
428431
SourceLoc loc, VisibleDeclConsumer &consumer)
429-
: SM(SM), DC(DC), Loc(loc), ChainedConsumer(consumer) {}
432+
: SM(SM), DC(DC), typeContext(DC->getInnermostTypeContext()), Loc(loc),
433+
ChainedConsumer(consumer) {}
430434

431435
void foundDecl(ValueDecl *D, DeclVisibilityKind reason,
432436
DynamicLookupInfo dynamicLookupInfo) override;

lib/AST/NameLookup.cpp

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,18 +172,150 @@ void UsableFilteringDeclConsumer::foundDecl(ValueDecl *D,
172172

173173
switch (reason) {
174174
case DeclVisibilityKind::LocalVariable:
175-
// Skip if Loc is before the found decl, unless its a TypeDecl (whose use
176-
// before its declaration is still allowed)
177-
if (!isa<TypeDecl>(D) && !SM.isBeforeInBuffer(D->getLoc(), Loc))
175+
case DeclVisibilityKind::FunctionParameter:
176+
// Skip if Loc is before the found decl if the decl is a var/let decl.
177+
// Type and func decls can be referenced before its declaration, or from
178+
// within nested type decls.
179+
if (isa<VarDecl>(D)) {
180+
if (reason == DeclVisibilityKind::LocalVariable) {
181+
// Workaround for fast-completion. A loc in the current context might be
182+
// in a loc
183+
auto tmpLoc = Loc;
184+
if (D->getDeclContext() != DC) {
185+
if (auto *contextD = DC->getAsDecl())
186+
tmpLoc = contextD->getStartLoc();
187+
}
188+
if (!SM.isBeforeInBuffer(D->getLoc(), Loc))
189+
return;
190+
}
191+
192+
// A type context cannot close over values defined in outer type contexts.
193+
if (D->getDeclContext()->getInnermostTypeContext() != typeContext)
194+
return;
195+
}
196+
break;
197+
198+
case DeclVisibilityKind::MemberOfOutsideNominal:
199+
// A type context cannot close over members of outer type contexts, except
200+
// for type decls.
201+
if (!isa<TypeDecl>(D) && !D->isStatic())
178202
return;
179203
break;
180-
default:
204+
205+
case DeclVisibilityKind::MemberOfCurrentNominal:
206+
case DeclVisibilityKind::MemberOfSuper:
207+
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
208+
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
209+
case DeclVisibilityKind::DynamicLookup:
210+
// Members on 'Self' including inherited/derived ones are always usable.
211+
break;
212+
213+
case DeclVisibilityKind::GenericParameter:
214+
// Generic params are type decls and are always usable from nested context.
215+
break;
216+
217+
case DeclVisibilityKind::VisibleAtTopLevel:
181218
// The rest of the file is currently skipped, so no need to check
182-
// decl location for VisibleAtTopLevel. Other visibility kinds are always
183-
// usable
219+
// decl location for VisibleAtTopLevel.
184220
break;
185221
}
186222

223+
// Filter out shadowed decls. Do this for only usable values even though
224+
// unusable values actually can shadow outer values, because compilers might
225+
// be able to diagnose it with fix-it to add the qualification. E.g.
226+
// func foo(global: T) {}
227+
// struct Outer {
228+
// func foo(outer: T) {}
229+
// func test() {
230+
// struct Inner {
231+
// func test() {
232+
// <HERE>
233+
// }
234+
// }
235+
// }
236+
// }
237+
// In this case 'foo(global:)' is shadowed by 'foo(outer:)', but 'foo(outer:)'
238+
// is _not_ usable because it's outside the current type context, whereas
239+
// 'foo(global:)' is still usable with 'ModuleName.' qualification.
240+
// FIXME: (for code completion,) If a global value or a static type member is
241+
// shadowd, we should suggest it with prefix (e.g. 'ModuleName.value').
242+
auto inserted = SeenNames.insert({D->getBaseName(), {D, reason}});
243+
if (!inserted.second) {
244+
auto shadowingReason = inserted.first->second.second;
245+
auto *shadowingD = inserted.first->second.first;
246+
247+
// A type decl cannot have overloads, and shadows everything outside the
248+
// scope.
249+
if (isa<TypeDecl>(shadowingD))
250+
return;
251+
252+
switch (shadowingReason) {
253+
case DeclVisibilityKind::LocalVariable:
254+
case DeclVisibilityKind::FunctionParameter:
255+
// Local func and var/let with a conflicting name.
256+
// func foo() {
257+
// func value(arg: Int) {}
258+
// var value = ""
259+
// }
260+
// In this case, 'var value' wins, regardless of their source order.
261+
// So, for confilicting local values in the same decl context, even if the
262+
// 'var value' is reported after 'func value', don't shadow it, but we
263+
// shadow everything with the name after that.
264+
if (reason == DeclVisibilityKind::LocalVariable &&
265+
isa<VarDecl>(D) && !isa<VarDecl>(shadowingD) &&
266+
shadowingD->getDeclContext() == D->getDeclContext()) {
267+
// Replace the shadowing decl so we shadow subsequent conflicting decls.
268+
inserted.first->second = {D, reason};
269+
break;
270+
}
271+
272+
// Otherwise, a local value shadows everything outside the scope.
273+
return;
274+
275+
case DeclVisibilityKind::GenericParameter:
276+
// A Generic parameter is a type name. It shadows everything outside the
277+
// generic context.
278+
return;
279+
280+
case DeclVisibilityKind::MemberOfCurrentNominal:
281+
case DeclVisibilityKind::MemberOfSuper:
282+
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
283+
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
284+
case DeclVisibilityKind::DynamicLookup:
285+
switch (reason) {
286+
case DeclVisibilityKind::MemberOfCurrentNominal:
287+
case DeclVisibilityKind::MemberOfSuper:
288+
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
289+
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
290+
case DeclVisibilityKind::DynamicLookup:
291+
// Members on the current type context don't shadow members with the
292+
// same base name on the current type contxt. They are overloads.
293+
break;
294+
default:
295+
// Members of a type context shadows values/types outside.
296+
return;
297+
}
298+
break;
299+
300+
case DeclVisibilityKind::MemberOfOutsideNominal:
301+
// For static values, it's unclear _which_ type context (i.e. this type,
302+
// super classes, conforming protocols) this decl was found in. For now,
303+
// consider all the outer nominals are the same.
304+
305+
if (reason == DeclVisibilityKind::MemberOfOutsideNominal)
306+
break;
307+
308+
// Values outside the nominal are shadowed.
309+
return;
310+
311+
case DeclVisibilityKind::VisibleAtTopLevel:
312+
// Top level decls don't shadow anything.
313+
// Well, that's not true. Decls in the current module shadows decls in
314+
// the imported modules. But we don't care them here.
315+
break;
316+
}
317+
}
318+
187319
ChainedConsumer.foundDecl(D, reason, dynamicLookupInfo);
188320
}
189321

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer,
11781178
const DeclContext *DC,
11791179
bool IncludeTopLevel, SourceLoc Loc) {
11801180
const SourceManager &SM = DC->getASTContext().SourceMgr;
1181-
auto Reason = DeclVisibilityKind::MemberOfCurrentNominal;
1181+
auto MemberReason = DeclVisibilityKind::MemberOfCurrentNominal;
11821182

11831183
// If we are inside of a method, check to see if there are any ivars in scope,
11841184
// and if so, whether this is a reference to one of them.
@@ -1280,12 +1280,15 @@ static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer,
12801280
dcGenericParams = dcGenericParams->getOuterParameters();
12811281
}
12821282

1283-
if (ExtendedType)
1284-
::lookupVisibleMemberDecls(ExtendedType, Consumer, DC, LS, Reason,
1283+
if (ExtendedType) {
1284+
::lookupVisibleMemberDecls(ExtendedType, Consumer, DC, LS, MemberReason,
12851285
nullptr);
12861286

1287+
// Going outside the current type context.
1288+
MemberReason = DeclVisibilityKind::MemberOfOutsideNominal;
1289+
}
1290+
12871291
DC = DC->getParent();
1288-
Reason = DeclVisibilityKind::MemberOfOutsideNominal;
12891292
}
12901293

12911294
if (auto SF = dyn_cast<SourceFile>(DC)) {

test/Constraints/tuple_arguments.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1712,7 +1712,6 @@ class Mappable<T> {
17121712
}
17131713

17141714
let x = Mappable(())
1715-
// expected-note@-1 4{{'x' declared here}}
17161715
x.map { (_: Void) in return () }
17171716
x.map { (_: ()) in () }
17181717

test/IDE/complete_escaped_keyword.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ enum MyEnum {
6565
func testInstance(val: MyEnum) {
6666
let _ = #^INSTANCE_PRIMARY^#
6767
// INSTANCE_PRIMARY: Begin completion
68+
// INSTANCE_PRIMARY-NOT: self[#Int#];
6869
// INSTANCE_PRIMARY-DAG: Decl[LocalVar]/Local: self[#MyEnum#]; name=self
69-
// INSTANCE_PRIMARY-DAG: Decl[InstanceVar]/CurrNominal: self[#Int#]; name=self
70-
// FIXME: ^ This is shadowed. We should hide this.
7170
// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit:)
7271
// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#else: String#})[#Int#]; name=`if`(else:)
72+
// INSTANCE_PRIMARY-NOT: self[#Int#];
7373
// INSTANCE_PRIMARY: End completion
7474

7575
let _ = self#^INSTANCE_SELF_NODOT^#

test/IDE/complete_in_accessors.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,8 @@ func accessorsInFunction(_ functionParam: Int) {
345345
// ACCESSORS_IN_MEMBER_FUNC_2: Begin completions
346346
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[LocalVar]/Local: self[#AccessorsInMemberFunction#]
347347
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Identical\])?}}: functionParam[#Int#]
348-
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceVar]/OutNominal: instanceVar[#Double#]
349-
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceMethod]/OutNominal: instanceFunc({#(a): Int#})[#Float#]
348+
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Double#]
349+
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(a): Int#})[#Float#]
350350
// ACCESSORS_IN_MEMBER_FUNC_2: End completions
351351

352352
struct AccessorsInMemberFunction {

0 commit comments

Comments
 (0)