Skip to content

Commit 8053120

Browse files
committed
Sema: Fix a MemberImportVisibility regression in resolveDeclRefExpr().
The changes from #84259 caused a regression in which declarations from an outer scope could be shadowed inappropriately by member declarations from a module that has not been imported. This fix addresses the issue by refactoring `resolveDeclRefExpr()` so that it performs lookups in two passes. First, it attempts to resolve the decl ref using the complete lookup algorithm and standard name lookup options, which will ignore members from modules that haven't been imported if `MemberImportVisibility` is enabled. Then, if no results were found it re-runs the full lookup algorithm again with `NameLookupFlags::IgnoreMissingImports` included to find additional results that ought to be diagnosed as unavailable. This insures that the unavailable results are not considered until after the main lookup has already failed. Resolves rdar://161078015.
1 parent 9953b1e commit 8053120

File tree

5 files changed

+211
-137
lines changed

5 files changed

+211
-137
lines changed

lib/Sema/PreCheckTarget.cpp

Lines changed: 164 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,12 @@ diagnoseUnqualifiedInit(UnresolvedDeclRefExpr *initExpr, DeclContext *dc,
518518
initExpr->getNameLoc(), /*implicit=*/true);
519519
}
520520

521-
/// Bind an UnresolvedDeclRefExpr by performing name lookup and
522-
/// returning the resultant expression. Context is the DeclContext used
523-
/// for the lookup.
524-
Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
525-
DeclContext *DC) {
521+
/// Bind an UnresolvedDeclRefExpr by performing name lookup using the given
522+
/// options and returning the resultant expression. `DC` is the DeclContext
523+
/// used for the lookup. If the lookup doesn't find any results, returns
524+
/// `nullptr`.
525+
static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC,
526+
NameLookupOptions lookupOptions) {
526527
auto &Context = DC->getASTContext();
527528
DeclNameRef Name = UDRE->getName();
528529
SourceLoc Loc = UDRE->getLoc();
@@ -562,14 +563,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
562563
LookupName = DeclNameRef(lookupName);
563564
}
564565

565-
// Perform standard value name lookup.
566-
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
567-
// TODO: Include all of the possible members to give a solver a
568-
// chance to diagnose name shadowing which requires explicit
569-
// name/module qualifier to access top-level name.
570-
lookupOptions |= NameLookupFlags::IncludeOuterResults;
571-
lookupOptions |= NameLookupFlags::IgnoreMissingImports;
572-
573566
LookupResult Lookup;
574567

575568
bool AllDeclRefs = true;
@@ -628,17 +621,8 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
628621
}
629622

630623
if (!Lookup) {
631-
// If we failed lookup of an operator, check to see if this is a range
632-
// operator misspelling. Otherwise try to diagnose a juxtaposition
633-
// e.g. (x*-4) that needs whitespace.
634-
if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) ||
635-
diagnoseIncDecOperator(Context.Diags, UDRE) ||
636-
diagnoseOperatorJuxtaposition(UDRE, DC) ||
637-
diagnoseNonexistentPowerOperator(Context.Diags, UDRE, DC)) {
638-
return errorResult();
639-
}
640-
641-
// Try ignoring access control.
624+
// For the purpose of diagnosing inaccessible results, try the lookup again
625+
// but ignore access control.
642626
NameLookupOptions relookupOptions = lookupOptions;
643627
relookupOptions |= NameLookupFlags::IgnoreAccessControl;
644628
auto inaccessibleResults =
@@ -663,117 +647,8 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
663647
return errorResult();
664648
}
665649

666-
// TODO: Name will be a compound name if it was written explicitly as
667-
// one, but we should also try to propagate labels into this.
668-
DeclNameLoc nameLoc = UDRE->getNameLoc();
669-
670-
Identifier simpleName = Name.getBaseIdentifier();
671-
const char *buffer = simpleName.get();
672-
llvm::SmallString<64> expectedIdentifier;
673-
bool isConfused = false;
674-
uint32_t codepoint;
675-
uint32_t firstConfusableCodepoint = 0;
676-
int totalCodepoints = 0;
677-
int offset = 0;
678-
while ((codepoint = validateUTF8CharacterAndAdvance(buffer,
679-
buffer +
680-
strlen(buffer)))
681-
!= ~0U) {
682-
int length = (buffer - simpleName.get()) - offset;
683-
if (auto expectedCodepoint =
684-
confusable::tryConvertConfusableCharacterToASCII(codepoint)) {
685-
if (firstConfusableCodepoint == 0) {
686-
firstConfusableCodepoint = codepoint;
687-
}
688-
isConfused = true;
689-
expectedIdentifier += expectedCodepoint;
690-
} else {
691-
expectedIdentifier += (char)codepoint;
692-
}
693-
694-
totalCodepoints++;
695-
696-
offset += length;
697-
}
698-
699-
auto emitBasicError = [&] {
700-
701-
if (Name.isSimpleName(Context.Id_self)) {
702-
// `self` gets diagnosed with a different error when it can't be found.
703-
Context.Diags
704-
.diagnose(Loc, diag::cannot_find_self_in_scope)
705-
.highlight(UDRE->getSourceRange());
706-
} else {
707-
Context.Diags
708-
.diagnose(Loc, diag::cannot_find_in_scope, Name,
709-
Name.isOperator())
710-
.highlight(UDRE->getSourceRange());
711-
}
712-
713-
if (!Context.LangOpts.DisableExperimentalClangImporterDiagnostics) {
714-
Context.getClangModuleLoader()->diagnoseTopLevelValue(
715-
Name.getFullName());
716-
}
717-
};
718-
719-
if (!isConfused) {
720-
if (Name.isSimpleName(Context.Id_Self)) {
721-
if (DeclContext *typeContext = DC->getInnermostTypeContext()){
722-
Type SelfType = typeContext->getSelfInterfaceType();
723-
724-
if (typeContext->getSelfClassDecl() &&
725-
!typeContext->getSelfClassDecl()->isForeignReferenceType())
726-
SelfType = DynamicSelfType::get(SelfType, Context);
727-
return new (Context)
728-
TypeExpr(new (Context) SelfTypeRepr(SelfType, Loc));
729-
}
730-
}
731-
732-
TypoCorrectionResults corrections(Name, nameLoc);
733-
734-
// FIXME: Don't perform typo correction inside macro arguments, because it
735-
// will invoke synthesizing declarations in this scope, which will attempt to
736-
// expand this macro which leads to circular reference errors.
737-
if (!namelookup::isInMacroArgument(DC->getParentSourceFile(), UDRE->getLoc())) {
738-
TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(),
739-
lookupOptions, corrections);
740-
}
741-
742-
if (auto typo = corrections.claimUniqueCorrection()) {
743-
auto diag = Context.Diags.diagnose(
744-
Loc, diag::cannot_find_in_scope_corrected, Name,
745-
Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str());
746-
diag.highlight(UDRE->getSourceRange());
747-
typo->addFixits(diag);
748-
} else {
749-
emitBasicError();
750-
}
751-
752-
corrections.noteAllCandidates();
753-
} else {
754-
emitBasicError();
755-
756-
if (totalCodepoints == 1) {
757-
auto charNames = confusable::getConfusableAndBaseCodepointNames(
758-
firstConfusableCodepoint);
759-
Context.Diags
760-
.diagnose(Loc, diag::single_confusable_character,
761-
UDRE->getName().isOperator(), simpleName.str(),
762-
charNames.first, expectedIdentifier, charNames.second)
763-
.fixItReplace(Loc, expectedIdentifier);
764-
} else {
765-
Context.Diags
766-
.diagnose(Loc, diag::confusable_character,
767-
UDRE->getName().isOperator(), simpleName.str(),
768-
expectedIdentifier)
769-
.fixItReplace(Loc, expectedIdentifier);
770-
}
771-
}
772-
773-
// TODO: consider recovering from here. We may want some way to suppress
774-
// downstream diagnostics, though.
775-
776-
return errorResult();
650+
// No results were found.
651+
return nullptr;
777652
}
778653

779654
// FIXME: Need to refactor the way we build an AST node from a lookup result!
@@ -892,8 +767,9 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
892767
UDRE->getNameLoc().getStartLoc());
893768
}
894769

895-
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
896-
UDRE->isImplicit(), UDRE->getFunctionRefInfo());
770+
return TypeChecker::buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
771+
UDRE->isImplicit(),
772+
UDRE->getFunctionRefInfo());
897773
}
898774

899775
ResultValues.clear();
@@ -983,6 +859,157 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
983859
return errorResult();
984860
}
985861

862+
Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
863+
DeclContext *DC) {
864+
auto &Context = DC->getASTContext();
865+
866+
// Perform standard value name lookup.
867+
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
868+
// TODO: Include all of the possible members to give a solver a
869+
// chance to diagnose name shadowing which requires explicit
870+
// name/module qualifier to access top-level name.
871+
lookupOptions |= NameLookupFlags::IncludeOuterResults;
872+
873+
Expr *result = ::resolveDeclRefExpr(UDRE, DC, lookupOptions);
874+
if (!result && Context.LangOpts.hasFeature(Feature::MemberImportVisibility,
875+
/*allowMigration=*/true)) {
876+
// If we didn't find a result, try again but this time relax
877+
// MemberImportVisibility restrictions. Note that diagnosing the missing
878+
// import is already handled by resolveDeclRefExpr().
879+
lookupOptions |= NameLookupFlags::IgnoreMissingImports;
880+
result = ::resolveDeclRefExpr(UDRE, DC, lookupOptions);
881+
}
882+
883+
if (result)
884+
return result;
885+
886+
// We didn't find any results. Look for common mistakes to diagnose.
887+
DeclNameRef Name = UDRE->getName();
888+
SourceLoc Loc = UDRE->getLoc();
889+
if (Loc.isInvalid())
890+
DC = DC->getModuleScopeContext();
891+
892+
auto errorResult = [&]() -> Expr * {
893+
return new (Context) ErrorExpr(UDRE->getSourceRange());
894+
};
895+
896+
// If we failed lookup of an operator, check to see if this is a range
897+
// operator misspelling. Otherwise try to diagnose a juxtaposition
898+
// e.g. (x*-4) that needs whitespace.
899+
if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) ||
900+
diagnoseIncDecOperator(Context.Diags, UDRE) ||
901+
diagnoseOperatorJuxtaposition(UDRE, DC) ||
902+
diagnoseNonexistentPowerOperator(Context.Diags, UDRE, DC)) {
903+
return errorResult();
904+
}
905+
906+
// TODO: Name will be a compound name if it was written explicitly as
907+
// one, but we should also try to propagate labels into this.
908+
DeclNameLoc nameLoc = UDRE->getNameLoc();
909+
910+
Identifier simpleName = Name.getBaseIdentifier();
911+
const char *buffer = simpleName.get();
912+
llvm::SmallString<64> expectedIdentifier;
913+
bool isConfused = false;
914+
uint32_t codepoint;
915+
uint32_t firstConfusableCodepoint = 0;
916+
int totalCodepoints = 0;
917+
int offset = 0;
918+
while ((codepoint = validateUTF8CharacterAndAdvance(
919+
buffer, buffer + strlen(buffer))) != ~0U) {
920+
int length = (buffer - simpleName.get()) - offset;
921+
if (auto expectedCodepoint =
922+
confusable::tryConvertConfusableCharacterToASCII(codepoint)) {
923+
if (firstConfusableCodepoint == 0) {
924+
firstConfusableCodepoint = codepoint;
925+
}
926+
isConfused = true;
927+
expectedIdentifier += expectedCodepoint;
928+
} else {
929+
expectedIdentifier += (char)codepoint;
930+
}
931+
932+
totalCodepoints++;
933+
934+
offset += length;
935+
}
936+
937+
auto emitBasicError = [&] {
938+
if (Name.isSimpleName(Context.Id_self)) {
939+
// `self` gets diagnosed with a different error when it can't be found.
940+
Context.Diags.diagnose(Loc, diag::cannot_find_self_in_scope)
941+
.highlight(UDRE->getSourceRange());
942+
} else {
943+
Context.Diags
944+
.diagnose(Loc, diag::cannot_find_in_scope, Name, Name.isOperator())
945+
.highlight(UDRE->getSourceRange());
946+
}
947+
948+
if (!Context.LangOpts.DisableExperimentalClangImporterDiagnostics) {
949+
Context.getClangModuleLoader()->diagnoseTopLevelValue(Name.getFullName());
950+
}
951+
};
952+
953+
if (!isConfused) {
954+
if (Name.isSimpleName(Context.Id_Self)) {
955+
if (DeclContext *typeContext = DC->getInnermostTypeContext()) {
956+
Type SelfType = typeContext->getSelfInterfaceType();
957+
958+
if (typeContext->getSelfClassDecl() &&
959+
!typeContext->getSelfClassDecl()->isForeignReferenceType())
960+
SelfType = DynamicSelfType::get(SelfType, Context);
961+
return new (Context)
962+
TypeExpr(new (Context) SelfTypeRepr(SelfType, Loc));
963+
}
964+
}
965+
966+
TypoCorrectionResults corrections(Name, nameLoc);
967+
968+
// FIXME: Don't perform typo correction inside macro arguments, because it
969+
// will invoke synthesizing declarations in this scope, which will attempt
970+
// to expand this macro which leads to circular reference errors.
971+
if (!namelookup::isInMacroArgument(DC->getParentSourceFile(),
972+
UDRE->getLoc())) {
973+
TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(),
974+
lookupOptions, corrections);
975+
}
976+
977+
if (auto typo = corrections.claimUniqueCorrection()) {
978+
auto diag = Context.Diags.diagnose(
979+
Loc, diag::cannot_find_in_scope_corrected, Name, Name.isOperator(),
980+
typo->CorrectedName.getBaseIdentifier().str());
981+
diag.highlight(UDRE->getSourceRange());
982+
typo->addFixits(diag);
983+
} else {
984+
emitBasicError();
985+
}
986+
987+
corrections.noteAllCandidates();
988+
} else {
989+
emitBasicError();
990+
991+
if (totalCodepoints == 1) {
992+
auto charNames = confusable::getConfusableAndBaseCodepointNames(
993+
firstConfusableCodepoint);
994+
Context.Diags
995+
.diagnose(Loc, diag::single_confusable_character,
996+
UDRE->getName().isOperator(), simpleName.str(),
997+
charNames.first, expectedIdentifier, charNames.second)
998+
.fixItReplace(Loc, expectedIdentifier);
999+
} else {
1000+
Context.Diags
1001+
.diagnose(Loc, diag::confusable_character,
1002+
UDRE->getName().isOperator(), simpleName.str(),
1003+
expectedIdentifier)
1004+
.fixItReplace(Loc, expectedIdentifier);
1005+
}
1006+
}
1007+
1008+
// TODO: consider recovering from here. We may want some way to suppress
1009+
// downstream diagnostics, though.
1010+
return errorResult();
1011+
}
1012+
9861013
/// If an expression references 'self.init' or 'super.init' in an
9871014
/// initializer context, returns the implicit 'self' decl of the constructor.
9881015
/// Otherwise, return nil.

test/NameLookup/Inputs/MemberImportVisibility/members_A.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ infix operator <<<
1616
infix operator >>>
1717
infix operator <>
1818

19+
@available(*, deprecated)
20+
public func shadowedByMemberOnXinA() { }
21+
22+
@available(*, deprecated)
23+
public func shadowedByMemberOnXinB() { }
24+
25+
@available(*, deprecated)
26+
public func shadowedByMemberOnXinC() { }
27+
28+
@available(*, deprecated)
29+
public func shadowedByStaticMemberOnXinA() { }
30+
31+
@available(*, deprecated)
32+
public func shadowedByStaticMemberOnXinB() { }
33+
34+
@available(*, deprecated)
35+
public func shadowedByStaticMemberOnXinC() { }
36+
1937
extension X {
2038
public func XinA() { }
2139

@@ -28,6 +46,9 @@ extension X {
2846

2947
public struct NestedInA {}
3048
public protocol ProtoNestedInA {}
49+
50+
public func shadowedByMemberOnXinA() { }
51+
public static func shadowedByStaticMemberOnXinA() { }
3152
}
3253

3354
extension Y {

test/NameLookup/Inputs/MemberImportVisibility/members_B.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ extension X {
1111
public var propXinB: Bool { return true }
1212
package var propXinB_package: Bool { return true }
1313

14+
public func shadowedByMemberOnXinB() { }
15+
public static func shadowedByStaticMemberOnXinB() { }
16+
17+
public static var max: Int { return Int.min }
18+
1419
public static func >>>(a: Self, b: Self) -> Self { b }
1520

1621
public struct NestedInB {}

test/NameLookup/Inputs/MemberImportVisibility/members_C.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ extension X {
1010

1111
public var propXinC: Bool { return true }
1212

13+
public func shadowedByMemberOnXinC() { }
14+
public static func shadowedByStaticMemberOnXinC() { }
15+
1316
public static func <>(a: Self, b: Self) -> Self { a }
1417

1518
public struct NestedInC {}

0 commit comments

Comments
 (0)