Skip to content

Commit a0a97c3

Browse files
committed
Support module selectors in CSDiagnostics
1 parent 152718c commit a0a97c3

File tree

7 files changed

+151
-15
lines changed

7 files changed

+151
-15
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: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ extension ModuleSelectorTestingKit::A: @retroactive Swift::Equatable {
4848
}
4949
else {
5050
self = ModuleSelectorTestingKit::A(value: .Swift::min)
51+
self = A.ModuleSelectorTestingKit::init(value: .min)
5152
}
5253

5354
self.main::myNegate()
@@ -115,6 +116,9 @@ extension B: main::Equatable {
115116
// expected-error@-1 {{declaration 'B' is not imported through module 'main'}}
116117
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{14-18=ModuleSelectorTestingKit}}
117118
// expected-error@-3 {{cannot infer contextual base in reference to member 'main::min'}}
119+
self = B.main::init(value: .min)
120+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'main'}}
121+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-20=ModuleSelectorTestingKit}}
118122
}
119123

120124
self.main::myNegate()
@@ -149,7 +153,7 @@ extension C: ModuleSelectorTestingKit::Equatable {
149153

150154
@_dynamicReplacement(for: ModuleSelectorTestingKit::negate())
151155
mutating func myNegate() {
152-
// FIXME improve: expected-note@-1 {{did you mean 'myNegate'?}}
156+
// expected-note@-1 {{'myNegate()' declared here}}
153157

154158
let fn: (ModuleSelectorTestingKit::Int, ModuleSelectorTestingKit::Int) -> ModuleSelectorTestingKit::Int =
155159
// expected-error@-1 3{{type 'Int' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -175,11 +179,14 @@ extension C: ModuleSelectorTestingKit::Equatable {
175179
}
176180
else {
177181
self = ModuleSelectorTestingKit::C(value: .ModuleSelectorTestingKit::min)
178-
// FIXME improve: expected-error@-1 {{type 'Int' has no member 'ModuleSelectorTestingKit::min'}}
182+
// expected-error@-1 {{declaration 'min' is not imported through module 'ModuleSelectorTestingKit'}}
183+
// expected-note@-2 {{did you mean module 'Swift'?}} {{50-74=Swift}}
184+
self = C.ModuleSelectorTestingKit::init(value: .min)
179185
}
180186

181187
self.ModuleSelectorTestingKit::myNegate()
182-
// FIXME improve: expected-error@-1 {{value of type 'C' has no member 'ModuleSelectorTestingKit::myNegate'}}
188+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'ModuleSelectorTestingKit'}}
189+
// expected-note@-2 {{did you mean module 'main'?}} {{10-34=main}}
183190
}
184191

185192
// FIXME: Can we test @convention(witness_method:)?
@@ -212,7 +219,7 @@ extension D: @retroactive Swift::Equatable {
212219
// expected-error@-1 {{replaced function 'Swift::negate()' could not be found}}
213220
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{29-34=ModuleSelectorTestingKit}}
214221
mutating func myNegate() {
215-
// FIXME improve: expected-note@-1 {{did you mean 'myNegate'?}}
222+
// expected-note@-1 {{'myNegate()' declared here}}
216223

217224
let fn: (Swift::Int, Swift::Int) -> Swift::Int =
218225
(Swift::+)
@@ -233,10 +240,14 @@ extension D: @retroactive Swift::Equatable {
233240
// expected-error@-1 {{declaration 'D' is not imported through module 'Swift'}}
234241
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{14-19=ModuleSelectorTestingKit}}
235242
// expected-error@-3 {{cannot infer contextual base in reference to member 'Swift::min'}}
243+
self = D.Swift::init(value: .min)
244+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'Swift'}}
245+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-21=ModuleSelectorTestingKit}}
236246
}
237247

238248
self.Swift::myNegate()
239-
// FIXME improve: expected-error@-1 {{value of type 'D' has no member 'Swift::myNegate'}}
249+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'Swift'}}
250+
// expected-note@-2 {{did you mean module 'main'?}} {{10-15=main}}
240251
}
241252

242253
// FIXME: Can we test @convention(witness_method:)?
@@ -467,8 +478,8 @@ func badModuleNames() {
467478
// FIXME redundant: expected-note@-3 {{did you mean module 'Swift'?}}
468479

469480
_ = "foo".NonexistentModule::count
470-
// FIXME improve: expected-error@-1 {{value of type 'String' has no member 'NonexistentModule::count'}}
471-
// FIXME: expected-EVENTUALLY-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
481+
// expected-error@-1 {{declaration 'count' is not imported through module 'NonexistentModule'}}
482+
// expected-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
472483

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

0 commit comments

Comments
 (0)