Skip to content

Commit 4df114e

Browse files
committed
Improve module selector constraint solver diagnostics
1 parent 8b5c217 commit 4df114e

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

@@ -1930,6 +1934,25 @@ class AllowInaccessibleMember final : public AllowInvalidMemberRef {
19301934
}
19311935
};
19321936

1937+
class AllowMemberFromWrongModule final : public AllowInvalidMemberRef {
1938+
AllowMemberFromWrongModule(ConstraintSystem &cs, Type baseType,
1939+
ValueDecl *member, DeclNameRef name,
1940+
ConstraintLocator *locator)
1941+
: AllowInvalidMemberRef(cs, FixKind::AllowMemberFromWrongModule, baseType,
1942+
member, name, locator) {}
1943+
1944+
public:
1945+
std::string getName() const override {
1946+
return "allow reference to member from wrong module";
1947+
}
1948+
1949+
bool diagnose(const Solution &solution, bool asNote = false) const override;
1950+
1951+
static AllowMemberFromWrongModule *create(ConstraintSystem &cs, Type baseType,
1952+
ValueDecl *member, DeclNameRef name,
1953+
ConstraintLocator *locator);
1954+
};
1955+
19331956
class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
19341957

19351958
AllowAnyObjectKeyPathRoot(ConstraintSystem &cs, ConstraintLocator *locator)

include/swift/Sema/ConstraintSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,9 @@ struct MemberLookupResult {
20332033
/// 'accesses' attributes of the init accessor and therefore canno
20342034
/// t be referenced in its body.
20352035
UR_UnavailableWithinInitAccessor,
2036+
2037+
/// The module selector in the `DeclNameRef` does not match this candidate.
2038+
UR_WrongModule
20362039
};
20372040

20382041
/// 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
@@ -6322,10 +6322,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
63226322

63236323
if (baseExpr) {
63246324
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
6325-
const auto &solution = getSolution();
6326-
if (llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
6327-
return fix->getLocator() == locator;
6328-
}))
6325+
if (hasFixFor(getSolution(), locator))
63296326
return false;
63306327
}
63316328

@@ -6345,6 +6342,54 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
63456342
return true;
63466343
}
63476344

6345+
bool MemberFromWrongModuleFailure::diagnoseAsError() {
6346+
auto anchor = getRawAnchor();
6347+
// Let's try to avoid over-diagnosing chains of wrong-module
6348+
// members e.g.:
6349+
//
6350+
// struct A {
6351+
// struct B {
6352+
// struct C {}
6353+
// }
6354+
// }
6355+
//
6356+
// _ = A.Other::B.Other::C()
6357+
Expr *baseExpr = nullptr;
6358+
DeclNameLoc nameLoc;
6359+
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
6360+
baseExpr = UDE->getBase();
6361+
nameLoc = UDE->getNameLoc();
6362+
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
6363+
nameLoc = UME->getNameLoc();
6364+
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
6365+
baseExpr = SE->getBase();
6366+
} else if (auto *call = getAsExpr<CallExpr>(anchor)) {
6367+
baseExpr = call->getFn();
6368+
}
6369+
6370+
if (baseExpr) {
6371+
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
6372+
if (hasFixFor(getSolution(), locator))
6373+
return false;
6374+
}
6375+
6376+
auto modSelLoc = nameLoc.getModuleSelectorLoc();
6377+
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : ::getLoc(anchor);
6378+
6379+
emitDiagnosticAt(loc, diag::decl_not_in_module, Member->getName(),
6380+
Name.getModuleSelector());
6381+
6382+
Identifier actualModuleName = Member->getModuleContext()->getName();
6383+
assert(actualModuleName != Name.getModuleSelector() &&
6384+
"Module selector failure on member in same module?");
6385+
emitDiagnosticAt(loc, diag::note_change_module_selector, actualModuleName)
6386+
.fixItReplace(modSelLoc, actualModuleName.str());
6387+
6388+
emitDiagnosticAt(Member, diag::decl_declared_here, Member);
6389+
6390+
return true;
6391+
}
6392+
63486393
SourceLoc AnyObjectKeyPathRootFailure::getLoc() const {
63496394
auto anchor = getAnchor();
63506395

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
@@ -1207,6 +1207,21 @@ AllowInaccessibleMember::create(ConstraintSystem &cs, Type baseType,
12071207
AllowInaccessibleMember(cs, baseType, member, name, locator);
12081208
}
12091209

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

lib/Sema/CSSimplify.cpp

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

1076210762
// If we have no viable or unviable candidates, and we're generating,
10763-
// diagnostics, rerun the query with inaccessible members included, so we can
10764-
// include them in the unviable candidates list.
10763+
// diagnostics, rerun the query with various excluded members included, so we
10764+
// can include them in the unviable candidates list.
1076510765
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
1076610766
includeInaccessibleMembers) {
1076710767
NameLookupOptions lookupOptions =
@@ -10770,7 +10770,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1077010770
// Local function that looks up additional candidates using the given lookup
1077110771
// options, recording the results as unviable candidates.
1077210772
auto lookupUnviable =
10773-
[&](NameLookupOptions lookupOptions,
10773+
[&](DeclNameRef memberName,
10774+
NameLookupOptions lookupOptions,
1077410775
MemberLookupResult::UnviableReason reason) -> bool {
1077510776
auto lookup = TypeChecker::lookupMember(DC, instanceTy, memberName,
1077610777
memberLoc, lookupOptions);
@@ -10794,9 +10795,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1079410795
return !lookup.empty();
1079510796
};
1079610797

10798+
// Look for members that were excluded because of a module selector.
10799+
if (memberName.hasModuleSelector()) {
10800+
DeclNameRef unqualifiedMemberName{memberName.getFullName()};
10801+
10802+
if (lookupUnviable(unqualifiedMemberName,
10803+
lookupOptions,
10804+
MemberLookupResult::UR_WrongModule))
10805+
return result;
10806+
}
10807+
1079710808
// Ignore access control so we get candidates that might have been missed
1079810809
// before.
10799-
if (lookupUnviable(lookupOptions | NameLookupFlags::IgnoreAccessControl,
10810+
if (lookupUnviable(memberName,
10811+
lookupOptions | NameLookupFlags::IgnoreAccessControl,
1080010812
MemberLookupResult::UR_Inaccessible))
1080110813
return result;
1080210814
}
@@ -10997,6 +11009,11 @@ static ConstraintFix *fixMemberRef(
1099711009
: nullptr;
1099811010
}
1099911011

11012+
case MemberLookupResult::UR_WrongModule:
11013+
assert(choice.isDecl());
11014+
return AllowMemberFromWrongModule::create(cs, baseTy, choice.getDecl(),
11015+
memberName, locator);
11016+
1100011017
case MemberLookupResult::UR_Inaccessible:
1100111018
assert(choice.isDecl());
1100211019
return AllowInaccessibleMember::create(cs, baseTy, choice.getDecl(),
@@ -15880,6 +15897,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1588015897
case FixKind::AllowInvalidInitRef:
1588115898
case FixKind::AllowClosureParameterDestructuring:
1588215899
case FixKind::AllowInaccessibleMember:
15900+
case FixKind::AllowMemberFromWrongModule:
1588315901
case FixKind::AllowAnyObjectKeyPathRoot:
1588415902
case FixKind::AllowMultiArgFuncKeyPathMismatch:
1588515903
case FixKind::TreatKeyPathSubscriptIndexAsHashable:

test/NameLookup/module_selector.swift

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

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

130130
self.main::myNegate()
@@ -163,7 +163,7 @@ extension C: @retroactive ModuleSelectorTestingKit::Equatable {
163163
@_dynamicReplacement(for: ModuleSelectorTestingKit::negate())
164164

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

168168
let fn: (ModuleSelectorTestingKit::Int, ModuleSelectorTestingKit::Int) -> ModuleSelectorTestingKit::Int =
169169
// expected-error@-1 3{{type 'Int' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -189,13 +189,15 @@ extension C: @retroactive ModuleSelectorTestingKit::Equatable {
189189
}
190190
else {
191191
self = ModuleSelectorTestingKit::C(value: .ModuleSelectorTestingKit::min)
192-
// FIXME improve: expected-error@-1 {{type 'Int' has no member 'ModuleSelectorTestingKit::min'}}
192+
// expected-error@-1 {{declaration 'min' is not imported through module 'ModuleSelectorTestingKit'}}
193+
// expected-note@-2 {{did you mean module 'Swift'?}} {{50-74=Swift}}
193194

194195
self = C.ModuleSelectorTestingKit::init(value: .min)
195196
}
196197

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

200202
ModuleSelectorTestingKit::fatalError()
201203
// expected-error@-1 {{declaration 'fatalError' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -231,7 +233,7 @@ extension D: @retroactive Swift::Equatable {
231233
// FIXME improve: expected-error@-1 {{replaced function 'Swift::negate()' could not be found}}
232234

233235
mutating func myNegate() {
234-
// expected-note@-1 {{did you mean 'myNegate'?}}
236+
// expected-note@-1 {{'myNegate()' declared here}}
235237

236238
let fn: (Swift::Int, Swift::Int) -> Swift::Int =
237239
(Swift::+)
@@ -255,12 +257,13 @@ extension D: @retroactive Swift::Equatable {
255257
// expected-error@-3 {{cannot infer contextual base in reference to member 'Swift::min'}}
256258

257259
self = D.Swift::init(value: .min)
258-
// FIXME improve: expected-error@-1 {{'D' cannot be constructed because it has no accessible initializers}}
259-
// expected-error@-2 {{cannot infer contextual base in reference to member 'min'}}
260+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'Swift'}}
261+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-21=ModuleSelectorTestingKit}}
260262
}
261263

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

265268
Swift::fatalError()
266269
}
@@ -490,8 +493,8 @@ func badModuleNames() {
490493
// FIXME redundant: expected-note@-3 {{did you mean module 'Swift'?}}
491494

492495
_ = "foo".NonexistentModule::count
493-
// FIXME improve: expected-error@-1 {{value of type 'String' has no member 'NonexistentModule::count'}}
494-
// FIXME: expected-EVENTUALLY-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
496+
// expected-error@-1 {{declaration 'count' is not imported through module 'NonexistentModule'}}
497+
// expected-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
495498

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

0 commit comments

Comments
 (0)