Skip to content

Commit 8f492c9

Browse files
committed
Improve module selector constraint solver diagnostics
1 parent 5af89a2 commit 8f492c9

File tree

7 files changed

+147
-19
lines changed

7 files changed

+147
-19
lines changed

include/swift/Sema/CSFix.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ enum class FixKind : uint8_t {
186186
/// no access control.
187187
AllowInaccessibleMember,
188188

189+
/// If a module selector prevented us from selecting a member, pretend that it
190+
/// was not specified.
191+
AllowMemberFromWrongModule,
192+
189193
/// Allow KeyPaths to use AnyObject as root type
190194
AllowAnyObjectKeyPathRoot,
191195

@@ -1938,6 +1942,25 @@ class AllowInaccessibleMember final : public AllowInvalidMemberRef {
19381942
}
19391943
};
19401944

1945+
class AllowMemberFromWrongModule final : public AllowInvalidMemberRef {
1946+
AllowMemberFromWrongModule(ConstraintSystem &cs, Type baseType,
1947+
ValueDecl *member, DeclNameRef name,
1948+
ConstraintLocator *locator)
1949+
: AllowInvalidMemberRef(cs, FixKind::AllowMemberFromWrongModule, baseType,
1950+
member, name, locator) {}
1951+
1952+
public:
1953+
std::string getName() const override {
1954+
return "allow reference to member from wrong module";
1955+
}
1956+
1957+
bool diagnose(const Solution &solution, bool asNote = false) const override;
1958+
1959+
static AllowMemberFromWrongModule *create(ConstraintSystem &cs, Type baseType,
1960+
ValueDecl *member, DeclNameRef name,
1961+
ConstraintLocator *locator);
1962+
};
1963+
19411964
class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
19421965

19431966
AllowAnyObjectKeyPathRoot(ConstraintSystem &cs, ConstraintLocator *locator)

include/swift/Sema/ConstraintSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,9 @@ struct MemberLookupResult {
20622062
/// 'accesses' attributes of the init accessor and therefore canno
20632063
/// t be referenced in its body.
20642064
UR_UnavailableWithinInitAccessor,
2065+
2066+
/// The module selector in the `DeclNameRef` does not match this candidate.
2067+
UR_WrongModule
20652068
};
20662069

20672070
/// This is a list of considered (but rejected) candidates, along with a

lib/Sema/CSDiagnostics.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6349,10 +6349,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
63496349

63506350
if (baseExpr) {
63516351
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
6352-
const auto &solution = getSolution();
6353-
if (llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
6354-
return fix->getLocator() == locator;
6355-
}))
6352+
if (hasFixFor(getSolution(), locator))
63566353
return false;
63576354
}
63586355

@@ -6372,6 +6369,54 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
63726369
return true;
63736370
}
63746371

6372+
bool MemberFromWrongModuleFailure::diagnoseAsError() {
6373+
auto anchor = getRawAnchor();
6374+
// Let's try to avoid over-diagnosing chains of wrong-module
6375+
// members e.g.:
6376+
//
6377+
// struct A {
6378+
// struct B {
6379+
// struct C {}
6380+
// }
6381+
// }
6382+
//
6383+
// _ = A.Other::B.Other::C()
6384+
Expr *baseExpr = nullptr;
6385+
DeclNameLoc nameLoc;
6386+
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
6387+
baseExpr = UDE->getBase();
6388+
nameLoc = UDE->getNameLoc();
6389+
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
6390+
nameLoc = UME->getNameLoc();
6391+
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
6392+
baseExpr = SE->getBase();
6393+
} else if (auto *call = getAsExpr<CallExpr>(anchor)) {
6394+
baseExpr = call->getFn();
6395+
}
6396+
6397+
if (baseExpr) {
6398+
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
6399+
if (hasFixFor(getSolution(), locator))
6400+
return false;
6401+
}
6402+
6403+
auto modSelLoc = nameLoc.getModuleSelectorLoc();
6404+
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : ::getLoc(anchor);
6405+
6406+
emitDiagnosticAt(loc, diag::decl_not_in_module, Member->getName(),
6407+
Name.getModuleSelector());
6408+
6409+
Identifier actualModuleName = Member->getModuleContext()->getName();
6410+
assert(actualModuleName != Name.getModuleSelector() &&
6411+
"Module selector failure on member in same module?");
6412+
emitDiagnosticAt(loc, diag::note_change_module_selector, actualModuleName)
6413+
.fixItReplace(modSelLoc, actualModuleName.str());
6414+
6415+
emitDiagnosticAt(Member, diag::decl_declared_here, Member);
6416+
6417+
return true;
6418+
}
6419+
63756420
SourceLoc AnyObjectKeyPathRootFailure::getLoc() const {
63766421
auto anchor = getAnchor();
63776422

lib/Sema/CSDiagnostics.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,27 @@ class InaccessibleMemberFailure final : public FailureDiagnostic {
16441644
bool diagnoseAsError() override;
16451645
};
16461646

1647+
/// Diagnose an attempt to reference member from the wrong module with a module
1648+
/// selector, e.g.
1649+
///
1650+
/// ```swift
1651+
/// import Foo
1652+
/// import Bar
1653+
///
1654+
/// SomeType.Bar::methodDefinedInFoo()
1655+
/// ```
1656+
class MemberFromWrongModuleFailure final : public FailureDiagnostic {
1657+
ValueDecl *Member;
1658+
DeclNameRef Name;
1659+
1660+
public:
1661+
MemberFromWrongModuleFailure(const Solution &solution, DeclNameRef name,
1662+
ValueDecl *member, ConstraintLocator *locator)
1663+
: FailureDiagnostic(solution, locator), Member(member), Name(name) {}
1664+
1665+
bool diagnoseAsError() override;
1666+
};
1667+
16471668
/// Diagnose an attempt to reference member marked as `mutating`
16481669
/// on immutable base e.g. `let` variable:
16491670
///

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,21 @@ AllowInaccessibleMember::create(ConstraintSystem &cs, Type baseType,
12101210
AllowInaccessibleMember(cs, baseType, member, name, locator);
12111211
}
12121212

1213+
bool AllowMemberFromWrongModule::diagnose(const Solution &solution,
1214+
bool asNote) const {
1215+
MemberFromWrongModuleFailure failure(solution, getMemberName(), getMember(),
1216+
getLocator());
1217+
return failure.diagnose(asNote);
1218+
}
1219+
1220+
AllowMemberFromWrongModule *
1221+
AllowMemberFromWrongModule::create(ConstraintSystem &cs, Type baseType,
1222+
ValueDecl *member, DeclNameRef name,
1223+
ConstraintLocator *locator) {
1224+
return new (cs.getAllocator())
1225+
AllowMemberFromWrongModule(cs, baseType, member, name, locator);
1226+
}
1227+
12131228
bool AllowAnyObjectKeyPathRoot::diagnose(const Solution &solution,
12141229
bool asNote) const {
12151230
AnyObjectKeyPathRootFailure failure(solution, getLocator());

lib/Sema/CSSimplify.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10859,8 +10859,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1085910859
}
1086010860

1086110861
// If we have no viable or unviable candidates, and we're generating,
10862-
// diagnostics, rerun the query with inaccessible members included, so we can
10863-
// include them in the unviable candidates list.
10862+
// diagnostics, rerun the query with various excluded members included, so we
10863+
// can include them in the unviable candidates list.
1086410864
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
1086510865
includeInaccessibleMembers) {
1086610866
NameLookupOptions lookupOptions =
@@ -10869,7 +10869,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1086910869
// Local function that looks up additional candidates using the given lookup
1087010870
// options, recording the results as unviable candidates.
1087110871
auto lookupUnviable =
10872-
[&](NameLookupOptions lookupOptions,
10872+
[&](DeclNameRef memberName,
10873+
NameLookupOptions lookupOptions,
1087310874
MemberLookupResult::UnviableReason reason) -> bool {
1087410875
auto lookup = TypeChecker::lookupMember(DC, instanceTy, memberName,
1087510876
memberLoc, lookupOptions);
@@ -10893,9 +10894,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1089310894
return !lookup.empty();
1089410895
};
1089510896

10897+
// Look for members that were excluded because of a module selector.
10898+
if (memberName.hasModuleSelector()) {
10899+
DeclNameRef unqualifiedMemberName{memberName.getFullName()};
10900+
10901+
if (lookupUnviable(unqualifiedMemberName,
10902+
lookupOptions,
10903+
MemberLookupResult::UR_WrongModule))
10904+
return result;
10905+
}
10906+
1089610907
// Ignore access control so we get candidates that might have been missed
1089710908
// before.
10898-
if (lookupUnviable(lookupOptions | NameLookupFlags::IgnoreAccessControl,
10909+
if (lookupUnviable(memberName,
10910+
lookupOptions | NameLookupFlags::IgnoreAccessControl,
1089910911
MemberLookupResult::UR_Inaccessible))
1090010912
return result;
1090110913
}
@@ -11096,6 +11108,11 @@ static ConstraintFix *fixMemberRef(
1109611108
: nullptr;
1109711109
}
1109811110

11111+
case MemberLookupResult::UR_WrongModule:
11112+
assert(choice.isDecl());
11113+
return AllowMemberFromWrongModule::create(cs, baseTy, choice.getDecl(),
11114+
memberName, locator);
11115+
1109911116
case MemberLookupResult::UR_Inaccessible:
1110011117
assert(choice.isDecl());
1110111118
return AllowInaccessibleMember::create(cs, baseTy, choice.getDecl(),
@@ -16025,6 +16042,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1602516042
case FixKind::AllowInvalidInitRef:
1602616043
case FixKind::AllowClosureParameterDestructuring:
1602716044
case FixKind::AllowInaccessibleMember:
16045+
case FixKind::AllowMemberFromWrongModule:
1602816046
case FixKind::AllowAnyObjectKeyPathRoot:
1602916047
case FixKind::AllowMultiArgFuncKeyPathMismatch:
1603016048
case FixKind::TreatKeyPathSubscriptIndexAsHashable:

test/NameLookup/module_selector.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ extension B: @retroactive main::Equatable {
120120
// expected-error@-3 {{cannot infer contextual base in reference to member 'main::min'}}
121121

122122
self = B.main::init(value: .min)
123-
// FIXME improve: expected-error@-1 {{'B' cannot be constructed because it has no accessible initializers}}
124-
// expected-error@-2 {{cannot infer contextual base in reference to member 'min'}}
123+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'main'}}
124+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-20=ModuleSelectorTestingKit}}
125125
}
126126

127127
self.main::myNegate()
@@ -159,7 +159,7 @@ extension C: @retroactive ModuleSelectorTestingKit::Equatable {
159159
@_dynamicReplacement(for: ModuleSelectorTestingKit::negate())
160160

161161
mutating func myNegate() {
162-
// FIXME improve: expected-note@-1 {{did you mean 'myNegate'?}}
162+
// expected-note@-1 {{'myNegate()' declared here}}
163163

164164
let fn: (ModuleSelectorTestingKit::Int, ModuleSelectorTestingKit::Int) -> ModuleSelectorTestingKit::Int =
165165
// expected-error@-1 3{{type 'Int' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -185,13 +185,15 @@ extension C: @retroactive ModuleSelectorTestingKit::Equatable {
185185
}
186186
else {
187187
self = ModuleSelectorTestingKit::C(value: .ModuleSelectorTestingKit::min)
188-
// FIXME improve: expected-error@-1 {{type 'Int' has no member 'ModuleSelectorTestingKit::min'}}
188+
// expected-error@-1 {{declaration 'min' is not imported through module 'ModuleSelectorTestingKit'}}
189+
// expected-note@-2 {{did you mean module 'Swift'?}} {{50-74=Swift}}
189190

190191
self = C.ModuleSelectorTestingKit::init(value: .min)
191192
}
192193

193194
self.ModuleSelectorTestingKit::myNegate()
194-
// FIXME improve: expected-error@-1 {{value of type 'C' has no member 'ModuleSelectorTestingKit::myNegate'}}
195+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'ModuleSelectorTestingKit'}}
196+
// expected-note@-2 {{did you mean module 'main'?}} {{10-34=main}}
195197

196198
ModuleSelectorTestingKit::fatalError()
197199
// expected-error@-1 {{declaration 'fatalError' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -225,7 +227,7 @@ extension D: @retroactive Swift::Equatable {
225227
// FIXME improve: expected-error@-1 {{replaced function 'Swift::negate()' could not be found}}
226228

227229
mutating func myNegate() {
228-
// expected-note@-1 {{did you mean 'myNegate'?}}
230+
// expected-note@-1 {{'myNegate()' declared here}}
229231

230232
let fn: (Swift::Int, Swift::Int) -> Swift::Int =
231233
(Swift::+)
@@ -249,12 +251,13 @@ extension D: @retroactive Swift::Equatable {
249251
// expected-error@-3 {{cannot infer contextual base in reference to member 'Swift::min'}}
250252

251253
self = D.Swift::init(value: .min)
252-
// FIXME improve: expected-error@-1 {{'D' cannot be constructed because it has no accessible initializers}}
253-
// expected-error@-2 {{cannot infer contextual base in reference to member 'min'}}
254+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'Swift'}}
255+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-21=ModuleSelectorTestingKit}}
254256
}
255257

256258
self.Swift::myNegate()
257-
// FIXME improve: expected-error@-1 {{value of type 'D' has no member 'Swift::myNegate'}}
259+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'Swift'}}
260+
// expected-note@-2 {{did you mean module 'main'?}} {{10-15=main}}
258261

259262
Swift::fatalError()
260263
}
@@ -342,8 +345,8 @@ func badModuleNames() {
342345
// FIXME redundant: expected-note@-3 {{did you mean module 'Swift'?}}
343346

344347
_ = "foo".NonexistentModule::count
345-
// FIXME improve: expected-error@-1 {{value of type 'String' has no member 'NonexistentModule::count'}}
346-
// FIXME: expected-EVENTUALLY-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
348+
// expected-error@-1 {{declaration 'count' is not imported through module 'NonexistentModule'}}
349+
// expected-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
347350

348351
let x: NonexistentModule::MyType = NonexistentModule::MyType()
349352
// expected-error@-1 {{cannot find type 'NonexistentModule::MyType' in scope}}

0 commit comments

Comments
 (0)