Skip to content

Commit 2582c00

Browse files
authored
Merge pull request #23416 from xedin/diag-inaccessible-via-fixes-5.1
[5.1] [ConstraintSystem] Introduce a fix for inaccessible members
2 parents 8986bbb + c19144c commit 2582c00

File tree

15 files changed

+189
-33
lines changed

15 files changed

+189
-33
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -854,11 +854,17 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
854854
// visible to us, but the conforming type is. In this case, we need to
855855
// clamp the formal access for diagnostics purposes to the formal access
856856
// of the protocol itself.
857-
diagnose(nameLoc, diag::candidate_inaccessible, decl->getBaseName(),
858-
decl->getFormalAccessScope().accessLevelForDiagnostics());
859-
for (auto cand : result.UnviableCandidates)
860-
diagnose(cand.getDecl(), diag::decl_declared_here, memberName);
861-
857+
InaccessibleMemberFailure failure(expr, CS, decl,
858+
CS.getConstraintLocator(E));
859+
auto diagnosed = failure.diagnoseAsError();
860+
assert(diagnosed && "failed to produce expected diagnostic");
861+
for (auto cand : result.UnviableCandidates) {
862+
auto *choice = cand.getDecl();
863+
// failure is going to highlight candidate given to it,
864+
// we just need to handle the rest here.
865+
if (choice != decl)
866+
diagnose(choice, diag::decl_declared_here, choice->getFullName());
867+
}
862868
return;
863869
}
864870
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,3 +2246,57 @@ bool OutOfOrderArgumentFailure::diagnoseAsError() {
22462246

22472247
return true;
22482248
}
2249+
2250+
bool InaccessibleMemberFailure::diagnoseAsError() {
2251+
auto *anchor = getRawAnchor();
2252+
// Let's try to avoid over-diagnosing chains of inaccessible
2253+
// members e.g.:
2254+
//
2255+
// struct A {
2256+
// struct B {
2257+
// struct C {}
2258+
// }
2259+
// }
2260+
//
2261+
// _ = A.B.C()
2262+
//
2263+
// We'll have a fix for each `B', `C` and `C.init` but it makes
2264+
// sense to diagnose only `B` and consider the rest hidden.
2265+
Expr *baseExpr = nullptr;
2266+
DeclNameLoc nameLoc;
2267+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
2268+
baseExpr = UDE->getBase();
2269+
nameLoc = UDE->getNameLoc();
2270+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
2271+
nameLoc = UME->getNameLoc();
2272+
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
2273+
baseExpr = SE->getBase();
2274+
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
2275+
baseExpr = call->getFn();
2276+
}
2277+
2278+
if (baseExpr) {
2279+
auto &cs = getConstraintSystem();
2280+
auto *locator =
2281+
cs.getConstraintLocator(baseExpr, ConstraintLocator::Member);
2282+
if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) {
2283+
return fix->getLocator() == locator;
2284+
}))
2285+
return false;
2286+
}
2287+
2288+
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : anchor->getLoc();
2289+
auto accessLevel = Member->getFormalAccessScope().accessLevelForDiagnostics();
2290+
if (auto *CD = dyn_cast<ConstructorDecl>(Member)) {
2291+
emitDiagnostic(loc, diag::init_candidate_inaccessible,
2292+
CD->getResultInterfaceType(), accessLevel)
2293+
.highlight(nameLoc.getSourceRange());
2294+
} else {
2295+
emitDiagnostic(loc, diag::candidate_inaccessible, Member->getBaseName(),
2296+
accessLevel)
2297+
.highlight(nameLoc.getSourceRange());
2298+
}
2299+
2300+
emitDiagnostic(Member, diag::decl_declared_here, Member->getFullName());
2301+
return true;
2302+
}

lib/Sema/CSDiagnostics.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,29 @@ class ClosureParamDestructuringFailure final : public FailureDiagnostic {
956956
}
957957
};
958958

959+
/// Diagnose an attempt to reference inaccessible member e.g.
960+
///
961+
/// ```swift
962+
/// struct S {
963+
/// var foo: String
964+
///
965+
/// private init(_ v: String) {
966+
/// self.foo = v
967+
/// }
968+
/// }
969+
/// _ = S("ultimate question")
970+
/// ```
971+
class InaccessibleMemberFailure final : public FailureDiagnostic {
972+
ValueDecl *Member;
973+
974+
public:
975+
InaccessibleMemberFailure(Expr *root, ConstraintSystem &cs, ValueDecl *member,
976+
ConstraintLocator *locator)
977+
: FailureDiagnostic(root, cs, locator), Member(member) {}
978+
979+
bool diagnoseAsError() override;
980+
};
981+
959982
} // end namespace constraints
960983
} // end namespace swift
961984

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,3 +384,15 @@ MoveOutOfOrderArgument *MoveOutOfOrderArgument::create(
384384
return new (cs.getAllocator())
385385
MoveOutOfOrderArgument(cs, argIdx, prevArgIdx, bindings, locator);
386386
}
387+
388+
bool AllowInaccessibleMember::diagnose(Expr *root, bool asNote) const {
389+
InaccessibleMemberFailure failure(root, getConstraintSystem(), Member,
390+
getLocator());
391+
return failure.diagnose(asNote);
392+
}
393+
394+
AllowInaccessibleMember *
395+
AllowInaccessibleMember::create(ConstraintSystem &cs, ValueDecl *member,
396+
ConstraintLocator *locator) {
397+
return new (cs.getAllocator()) AllowInaccessibleMember(cs, member, locator);
398+
}

lib/Sema/CSFix.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ enum class FixKind : uint8_t {
132132

133133
/// If there is out-of-order argument, let's fix that by re-ordering.
134134
MoveOutOfOrderArgument,
135+
136+
/// If there is a matching inaccessible member - allow it as if there
137+
/// no access control.
138+
AllowInaccessibleMember,
135139
};
136140

137141
class ConstraintFix {
@@ -713,6 +717,26 @@ class MoveOutOfOrderArgument final : public ConstraintFix {
713717
ConstraintLocator *locator);
714718
};
715719

720+
class AllowInaccessibleMember final : public ConstraintFix {
721+
ValueDecl *Member;
722+
723+
AllowInaccessibleMember(ConstraintSystem &cs, ValueDecl *member,
724+
ConstraintLocator *locator)
725+
: ConstraintFix(cs, FixKind::AllowInaccessibleMember, locator),
726+
Member(member) {}
727+
728+
public:
729+
std::string getName() const override {
730+
return "allow inaccessible member reference";
731+
}
732+
733+
bool diagnose(Expr *root, bool asNote = false) const override;
734+
735+
static AllowInaccessibleMember *create(ConstraintSystem &cs,
736+
ValueDecl *member,
737+
ConstraintLocator *locator);
738+
};
739+
716740
} // end namespace constraints
717741
} // end namespace swift
718742

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4160,10 +4160,12 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
41604160
case MemberLookupResult::UR_TypeMemberOnInstance:
41614161
return AllowTypeOrInstanceMember::create(cs, baseTy, memberName, locator);
41624162

4163+
case MemberLookupResult::UR_Inaccessible:
4164+
return AllowInaccessibleMember::create(cs, decl, locator);
4165+
41634166
case MemberLookupResult::UR_MutatingMemberOnRValue:
41644167
case MemberLookupResult::UR_MutatingGetterOnRValue:
41654168
case MemberLookupResult::UR_LabelMismatch:
4166-
case MemberLookupResult::UR_Inaccessible:
41674169
case MemberLookupResult::UR_UnavailableInExistential:
41684170
break;
41694171
}
@@ -4187,9 +4189,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
41874189

41884190
auto locator = getConstraintLocator(locatorB);
41894191
MemberLookupResult result =
4190-
performMemberLookup(kind, member, baseTy, functionRefKind, locator,
4191-
/*includeInaccessibleMembers*/false);
4192-
4192+
performMemberLookup(kind, member, baseTy, functionRefKind, locator,
4193+
/*includeInaccessibleMembers*/ shouldAttemptFixes());
4194+
41934195
switch (result.OverallResult) {
41944196
case MemberLookupResult::Unsolved:
41954197
// If requested, generate a constraint.
@@ -6195,6 +6197,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
61956197
case FixKind::AllowInvalidInitRef:
61966198
case FixKind::AllowClosureParameterDestructuring:
61976199
case FixKind::MoveOutOfOrderArgument:
6200+
case FixKind::AllowInaccessibleMember:
61986201
llvm_unreachable("handled elsewhere");
61996202
}
62006203

lib/Sema/CalleeCandidateInfo.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "ConstraintSystem.h"
1818
#include "CSDiag.h"
19+
#include "CSDiagnostics.h"
1920
#include "CalleeCandidateInfo.h"
2021
#include "TypeCheckAvailability.h"
2122
#include "swift/AST/GenericEnvironment.h"
@@ -1108,18 +1109,17 @@ bool CalleeCandidateInfo::diagnoseSimpleErrors(const Expr *E) {
11081109
if (closeness == CC_Inaccessible) {
11091110
auto decl = candidates[0].getDecl();
11101111
assert(decl && "Only decl-based candidates may be marked inaccessible");
1111-
if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
1112-
CS.TC.diagnose(loc, diag::init_candidate_inaccessible,
1113-
CD->getResultInterfaceType(), decl->getFormalAccess());
1114-
1115-
} else {
1116-
CS.TC.diagnose(loc, diag::candidate_inaccessible, decl->getBaseName(),
1117-
decl->getFormalAccess());
1118-
}
1112+
1113+
InaccessibleMemberFailure failure(
1114+
nullptr, CS, decl, CS.getConstraintLocator(const_cast<Expr *>(E)));
1115+
auto diagnosed = failure.diagnoseAsError();
1116+
assert(diagnosed && "failed to produce expected diagnostic");
1117+
11191118
for (auto cand : candidates) {
1120-
if (auto decl = cand.getDecl()) {
1121-
CS.TC.diagnose(decl, diag::decl_declared_here, decl->getFullName());
1122-
}
1119+
auto *candidate = cand.getDecl();
1120+
if (candidate && candidate != decl)
1121+
CS.TC.diagnose(candidate, diag::decl_declared_here,
1122+
candidate->getFullName());
11231123
}
11241124

11251125
return true;

test/Compatibility/optional_visibility.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
}
66

77
class Conforms : Opt {
8-
private func f(callback: () -> ()) {} // expected-note {{'f' declared here}}
8+
private func f(callback: () -> ()) {} // expected-note {{'f(callback:)' declared here}}
99
}
1010

1111
func g(x: Conforms) {

test/Constraints/construction.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,35 @@ func init_via_non_const_metatype(_ s1: S1.Type) {
177177
_ = s1(i: 42) // expected-error {{initializing from a metatype value must reference 'init' explicitly}} {{9-9=.init}}
178178
_ = s1.init(i: 42) // ok
179179
}
180+
181+
// rdar://problem/45535925 - diagnostic is attached to invalid source location
182+
func rdar_45535925() {
183+
struct S {
184+
var addr: String
185+
var port: Int
186+
187+
private init(addr: String, port: Int?) {
188+
// expected-note@-1 {{'init(addr:port:)' declared here}}
189+
self.addr = addr
190+
self.port = port ?? 31337
191+
}
192+
193+
private init(port: Int) {
194+
self.addr = "localhost"
195+
self.port = port
196+
}
197+
198+
private func foo(_: Int) {} // expected-note {{'foo' declared here}}
199+
private static func bar() {} // expected-note {{'bar()' declared here}}
200+
}
201+
202+
_ = S(addr: "localhost", port: nil)
203+
// expected-error@-1 {{'S' initializer is inaccessible due to 'private' protection level}}
204+
205+
func baz(_ s: S) {
206+
s.foo(42)
207+
// expected-error@-1 {{'foo' is inaccessible due to 'private' protection level}}
208+
S.bar()
209+
// expected-error@-1 {{'bar' is inaccessible due to 'private' protection level}}
210+
}
211+
}

test/Constraints/dynamic_lookup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ let uopt : AnyObject! = nil
217217
uopt.wibble!()
218218

219219
// Should not be able to see private or internal @objc methods.
220-
uopt.privateFoo!() // expected-error{{value of type 'AnyObject?' has no member 'privateFoo'}}
221-
uopt.internalFoo!() // expected-error{{value of type 'AnyObject?' has no member 'internalFoo'}}
220+
uopt.privateFoo!() // expected-error{{'privateFoo' is inaccessible due to 'private' protection level}}
221+
uopt.internalFoo!() // expected-error{{'internalFoo' is inaccessible due to 'internal' protection level}}
222222

223223
let anyValue: Any = X()
224224
_ = anyValue.bar() // expected-error {{value of type 'Any' has no member 'bar'}}

test/Constraints/super_constructor.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ class B {
5555

5656
// SR-2484: Bad diagnostic for incorrectly calling private init
5757
class SR_2484 {
58-
private init() {} // expected-note {{'init' declared here}}
59-
private init(a: Int) {} // expected-note {{'init' declared here}}
58+
private init() {} // expected-note {{'init()' declared here}}
59+
private init(a: Int) {}
6060
}
6161

6262
class Impl_2484 : SR_2484 {
6363
init() {
64-
super.init() // expected-error {{'init' is inaccessible due to 'private' protection level}}
64+
super.init() // expected-error {{'SR_2484' initializer is inaccessible due to 'private' protection level}}
6565
}
6666
}
6767

test/NameBinding/Inputs/accessibility_other.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ private let c = 0
77
extension Foo {
88
public static func a() {}
99
internal static func b() {}
10-
private static func c() {} // expected-note {{'c' declared here}}
10+
private static func c() {} // expected-note {{'c()' declared here}}
1111
}
1212

1313
struct PrivateInit {

test/Sema/Inputs/accessibility_multi_other.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ private protocol PrivateProtocol {}
1414

1515
extension PrivateProtocol {
1616
public func publicExtensionMember() {}
17-
// expected-note@-1 {{'publicExtensionMember' declared here}}
17+
// expected-note@-1 {{'publicExtensionMember()' declared here}}
1818

1919
internal func internalExtensionMember() {}
20-
// expected-note@-1 {{'internalExtensionMember' declared here}}
20+
// expected-note@-1 {{'internalExtensionMember()' declared here}}
2121
}
2222

2323
fileprivate protocol FilePrivateProtocol {}
2424

2525
extension FilePrivateProtocol {
2626
public func publicFPExtensionMember() {}
27-
// expected-note@-1 {{'publicFPExtensionMember' declared here}}
27+
// expected-note@-1 {{'publicFPExtensionMember()' declared here}}
2828

2929
internal func internalFPExtensionMember() {}
30-
// expected-note@-1 {{'internalFPExtensionMember' declared here}}
30+
// expected-note@-1 {{'internalFPExtensionMember()' declared here}}
3131
}

test/decl/protocol/special/coding/class_codable_inheritance.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ class SimpleChildClass : SimpleClass {
3030

3131
// The enum should have a case for each of the vars.
3232
// NOTE: This expected error will need to be removed in the future.
33-
let _ = SimpleChildClass.CodingKeys.w // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}
33+
let _ = SimpleChildClass.CodingKeys.w
34+
// expected-error@-1 {{'CodingKeys' is inaccessible due to 'private' protection level}}
35+
// expected-error@-2 {{type 'SimpleClass.CodingKeys' has no member 'w'}}
3436

3537
// Inherited vars should not be part of the CodingKeys enum.
3638
// NOTE: This expected error will need to be updated in the future.

test/expr/primary/selector/Inputs/property_helper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ class OtherObjCClass: NSObject {
77

88
@objc internal func internalFunc() {}
99

10-
private func privateFunc() {} // expected-note 2{{'privateFunc' declared here}}
10+
private func privateFunc() {} // expected-note 2{{'privateFunc()' declared here}}
1111
}

0 commit comments

Comments
 (0)