Skip to content

Commit 427a581

Browse files
committed
Support module selectors in CSDiagnostics
1 parent 81091db commit 427a581

File tree

7 files changed

+173
-30
lines changed

7 files changed

+173
-30
lines changed

include/swift/Sema/CSFix.h

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

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

@@ -1519,6 +1523,25 @@ class AllowInaccessibleMember final : public AllowInvalidMemberRef {
15191523
ConstraintLocator *locator);
15201524
};
15211525

1526+
class AllowMemberFromWrongModule final : public AllowInvalidMemberRef {
1527+
AllowMemberFromWrongModule(ConstraintSystem &cs, Type baseType,
1528+
ValueDecl *member, DeclNameRef name,
1529+
ConstraintLocator *locator)
1530+
: AllowInvalidMemberRef(cs, FixKind::AllowMemberFromWrongModule, baseType,
1531+
member, name, locator) {}
1532+
1533+
public:
1534+
std::string getName() const override {
1535+
return "allow reference to member from wrong module";
1536+
}
1537+
1538+
bool diagnose(const Solution &solution, bool asNote = false) const override;
1539+
1540+
static AllowMemberFromWrongModule *create(ConstraintSystem &cs, Type baseType,
1541+
ValueDecl *member, DeclNameRef name,
1542+
ConstraintLocator *locator);
1543+
};
1544+
15221545
class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
15231546

15241547
AllowAnyObjectKeyPathRoot(ConstraintSystem &cs, ConstraintLocator *locator)

include/swift/Sema/ConstraintSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,9 @@ struct MemberLookupResult {
15001500
/// This is a static member being access through a protocol metatype
15011501
/// but its result type doesn't conform to this protocol.
15021502
UR_InvalidStaticMemberOnProtocolMetatype,
1503+
1504+
/// The module selector in the `DeclNameRef` does not match this candidate.
1505+
UR_WrongModule
15031506
};
15041507

15051508
/// 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
@@ -5316,10 +5316,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
53165316

53175317
if (baseExpr) {
53185318
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
5319-
const auto &solution = getSolution();
5320-
if (llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
5321-
return fix->getLocator() == locator;
5322-
}))
5319+
if (hasFixFor(getSolution(), locator))
53235320
return false;
53245321
}
53255322

@@ -5339,6 +5336,54 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
53395336
return true;
53405337
}
53415338

5339+
bool MemberFromWrongModuleFailure::diagnoseAsError() {
5340+
auto anchor = getRawAnchor();
5341+
// Let's try to avoid over-diagnosing chains of wrong-module
5342+
// members e.g.:
5343+
//
5344+
// struct A {
5345+
// struct B {
5346+
// struct C {}
5347+
// }
5348+
// }
5349+
//
5350+
// _ = A.Other::B.Other::C()
5351+
Expr *baseExpr = nullptr;
5352+
DeclNameLoc nameLoc;
5353+
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
5354+
baseExpr = UDE->getBase();
5355+
nameLoc = UDE->getNameLoc();
5356+
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
5357+
nameLoc = UME->getNameLoc();
5358+
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
5359+
baseExpr = SE->getBase();
5360+
} else if (auto *call = getAsExpr<CallExpr>(anchor)) {
5361+
baseExpr = call->getFn();
5362+
}
5363+
5364+
if (baseExpr) {
5365+
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
5366+
if (hasFixFor(getSolution(), locator))
5367+
return false;
5368+
}
5369+
5370+
auto modSelLoc = nameLoc.getModuleSelectorLoc();
5371+
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : ::getLoc(anchor);
5372+
5373+
emitDiagnosticAt(loc, diag::decl_not_in_module, Member->getName(),
5374+
Name.getModuleSelector());
5375+
5376+
Identifier actualModuleName = Member->getModuleContext()->getName();
5377+
assert(actualModuleName != Name.getModuleSelector() &&
5378+
"Module selector failure on member in same module?");
5379+
emitDiagnosticAt(loc, diag::note_change_module_selector, actualModuleName)
5380+
.fixItReplace(modSelLoc, actualModuleName.str());
5381+
5382+
emitDiagnosticAt(Member, diag::decl_declared_here, Member->getName());
5383+
5384+
return true;
5385+
}
5386+
53425387
SourceLoc AnyObjectKeyPathRootFailure::getLoc() const {
53435388
auto anchor = getAnchor();
53445389

lib/Sema/CSDiagnostics.h

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

1540+
/// Diagnose an attempt to reference member from the wrong module with a module
1541+
/// selector, e.g.
1542+
///
1543+
/// ```swift
1544+
/// import Foo
1545+
/// import Bar
1546+
///
1547+
/// SomeType.Bar::methodDefinedInFoo()
1548+
/// ```
1549+
class MemberFromWrongModuleFailure final : public FailureDiagnostic {
1550+
ValueDecl *Member;
1551+
DeclNameRef Name;
1552+
1553+
public:
1554+
MemberFromWrongModuleFailure(const Solution &solution, DeclNameRef name,
1555+
ValueDecl *member, ConstraintLocator *locator)
1556+
: FailureDiagnostic(solution, locator), Member(member), Name(name) {}
1557+
1558+
bool diagnoseAsError() override;
1559+
};
1560+
15401561
/// Diagnose an attempt to reference member marked as `mutating`
15411562
/// on immutable base e.g. `let` variable:
15421563
///

lib/Sema/CSFix.cpp

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

962+
bool AllowMemberFromWrongModule::diagnose(const Solution &solution,
963+
bool asNote) const {
964+
MemberFromWrongModuleFailure failure(solution, getMemberName(), getMember(),
965+
getLocator());
966+
return failure.diagnose(asNote);
967+
}
968+
969+
AllowMemberFromWrongModule *
970+
AllowMemberFromWrongModule::create(ConstraintSystem &cs, Type baseType,
971+
ValueDecl *member, DeclNameRef name,
972+
ConstraintLocator *locator) {
973+
return new (cs.getAllocator())
974+
AllowMemberFromWrongModule(cs, baseType, member, name, locator);
975+
}
976+
962977
bool AllowAnyObjectKeyPathRoot::diagnose(const Solution &solution,
963978
bool asNote) const {
964979
AnyObjectKeyPathRootFailure failure(solution, getLocator());

lib/Sema/CSSimplify.cpp

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7719,34 +7719,53 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
77197719
}
77207720

77217721
// If we have no viable or unviable candidates, and we're generating,
7722-
// diagnostics, rerun the query with inaccessible members included, so we can
7723-
// include them in the unviable candidates list.
7722+
// diagnostics, rerun the query with various excluded members included, so we
7723+
// can include them in the unviable candidates list.
77247724
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
77257725
includeInaccessibleMembers) {
7726+
auto addExcludedChoices = [&](LookupResult &lookup,
7727+
MemberLookupResult::UnviableReason reason) {
7728+
for (auto entry : lookup) {
7729+
auto *cand = entry.getValueDecl();
7730+
7731+
// If the result is invalid, skip it.
7732+
if (cand->isInvalid()) {
7733+
result.markErrorAlreadyDiagnosed();
7734+
return false;
7735+
}
7736+
7737+
if (excludedDynamicMembers.count(cand))
7738+
continue;
7739+
7740+
result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false,
7741+
/*isUnwrappedOptional=*/false),
7742+
reason);
7743+
}
7744+
return true;
7745+
};
7746+
7747+
// Look for members that were excluded because of a module selector.
7748+
if (memberName.hasModuleSelector()) {
7749+
DeclNameRef unqualifiedMemberName{memberName.getFullName()};
7750+
7751+
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
7752+
auto lookup =
7753+
TypeChecker::lookupMember(DC, instanceTy, unqualifiedMemberName,
7754+
lookupOptions);
7755+
if (!addExcludedChoices(lookup, MemberLookupResult::UR_WrongModule))
7756+
return result;
7757+
}
7758+
7759+
// Look for members that were excluded by access control.
77267760
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
7727-
7761+
77287762
// Ignore access control so we get candidates that might have been missed
77297763
// before.
77307764
lookupOptions |= NameLookupFlags::IgnoreAccessControl;
77317765

77327766
auto lookup =
77337767
TypeChecker::lookupMember(DC, instanceTy, memberName, lookupOptions);
7734-
for (auto entry : lookup) {
7735-
auto *cand = entry.getValueDecl();
7736-
7737-
// If the result is invalid, skip it.
7738-
if (cand->isInvalid()) {
7739-
result.markErrorAlreadyDiagnosed();
7740-
return result;
7741-
}
7742-
7743-
if (excludedDynamicMembers.count(cand))
7744-
continue;
7745-
7746-
result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false,
7747-
/*isUnwrappedOptional=*/false),
7748-
MemberLookupResult::UR_Inaccessible);
7749-
}
7768+
addExcludedChoices(lookup, MemberLookupResult::UR_Inaccessible);
77507769
}
77517770

77527771
return result;
@@ -7948,6 +7967,11 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
79487967
: nullptr;
79497968
}
79507969

7970+
case MemberLookupResult::UR_WrongModule:
7971+
assert(choice.isDecl());
7972+
return AllowMemberFromWrongModule::create(cs, baseTy, choice.getDecl(),
7973+
memberName, locator);
7974+
79517975
case MemberLookupResult::UR_Inaccessible:
79527976
assert(choice.isDecl());
79537977
return AllowInaccessibleMember::create(cs, baseTy, choice.getDecl(),
@@ -11637,6 +11661,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1163711661
case FixKind::AllowInvalidInitRef:
1163811662
case FixKind::AllowClosureParameterDestructuring:
1163911663
case FixKind::AllowInaccessibleMember:
11664+
case FixKind::AllowMemberFromWrongModule:
1164011665
case FixKind::AllowAnyObjectKeyPathRoot:
1164111666
case FixKind::AllowMultiArgFuncKeyPathMismatch:
1164211667
case FixKind::TreatKeyPathSubscriptIndexAsHashable:

test/NameLookup/module_selector.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extension ModuleSelectorTestingKit::A: Swift::Equatable {
4646
}
4747
else {
4848
self = ModuleSelectorTestingKit::A(value: .Swift::min)
49+
self = A.ModuleSelectorTestingKit::init(value: .min)
4950
}
5051

5152
self.main::myNegate()
@@ -112,6 +113,9 @@ extension B: main::Equatable {
112113
self = main::B(value: .main::min)
113114
// expected-error@-1 {{declaration 'B' is not imported through module 'main'}}
114115
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{14-18=ModuleSelectorTestingKit}}
116+
self = B.main::init(value: .min)
117+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'main'}}
118+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-20=ModuleSelectorTestingKit}}
115119
}
116120

117121
self.main::myNegate()
@@ -146,7 +150,7 @@ extension C: ModuleSelectorTestingKit::Equatable {
146150

147151
@_dynamicReplacement(for: ModuleSelectorTestingKit::negate())
148152
mutating func myNegate() {
149-
// FIXME improve: expected-note@-1 {{did you mean 'myNegate'?}}
153+
// expected-note@-1 {{'myNegate()' declared here}}
150154

151155
let fn: (ModuleSelectorTestingKit::Int, ModuleSelectorTestingKit::Int) -> ModuleSelectorTestingKit::Int =
152156
// expected-error@-1 3{{type 'Int' is not imported through module 'ModuleSelectorTestingKit'}}
@@ -172,11 +176,14 @@ extension C: ModuleSelectorTestingKit::Equatable {
172176
}
173177
else {
174178
self = ModuleSelectorTestingKit::C(value: .ModuleSelectorTestingKit::min)
175-
// FIXME improve: expected-error@-1 {{type 'Int' has no member 'ModuleSelectorTestingKit::min'}}
179+
// expected-error@-1 {{declaration 'min' is not imported through module 'ModuleSelectorTestingKit'}}
180+
// expected-note@-2 {{did you mean module 'Swift'?}} {{50-74=Swift}}
181+
self = C.ModuleSelectorTestingKit::init(value: .min)
176182
}
177183

178184
self.ModuleSelectorTestingKit::myNegate()
179-
// FIXME improve: expected-error@-1 {{value of type 'C' has no member 'ModuleSelectorTestingKit::myNegate'}}
185+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'ModuleSelectorTestingKit'}}
186+
// expected-note@-2 {{did you mean module 'main'?}} {{10-34=main}}
180187
}
181188

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

214221
let fn: (Swift::Int, Swift::Int) -> Swift::Int =
215222
(Swift::+)
@@ -229,10 +236,14 @@ extension D: Swift::Equatable {
229236
self = Swift::D(value: .ModuleSelectorTestingKit::min)
230237
// expected-error@-1 {{declaration 'D' is not imported through module 'Swift'}}
231238
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{14-19=ModuleSelectorTestingKit}}
239+
self = D.Swift::init(value: .min)
240+
// expected-error@-1 {{declaration 'init(value:)' is not imported through module 'Swift'}}
241+
// expected-note@-2 {{did you mean module 'ModuleSelectorTestingKit'?}} {{16-21=ModuleSelectorTestingKit}}
232242
}
233243

234244
self.Swift::myNegate()
235-
// FIXME improve: expected-error@-1 {{value of type 'D' has no member 'Swift::myNegate'}}
245+
// expected-error@-1 {{declaration 'myNegate()' is not imported through module 'Swift'}}
246+
// expected-note@-2 {{did you mean module 'main'?}} {{10-15=main}}
236247
}
237248

238249
// FIXME: Can we test @convention(witness_method:)?
@@ -463,8 +474,8 @@ func badModuleNames() {
463474
// FIXME redundant: expected-note@-3 {{did you mean module 'Swift'?}}
464475

465476
_ = "foo".NonexistentModule::count
466-
// FIXME improve: expected-error@-1 {{value of type 'String' has no member 'NonexistentModule::count'}}
467-
// FIXME: expected-EVENTUALLY-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
477+
// expected-error@-1 {{declaration 'count' is not imported through module 'NonexistentModule'}}
478+
// expected-note@-2 {{did you mean module 'Swift'?}} {{13-30=Swift}}
468479

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

0 commit comments

Comments
 (0)