Skip to content

Commit 36a9940

Browse files
authored
Better error message when accessing private members (#18690)
Fixes #18686
2 parents 89fa247 + 6c1fce0 commit 36a9940

File tree

14 files changed

+156
-29
lines changed

14 files changed

+156
-29
lines changed
Submodule stdLib213 updated 489 files

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

+4-17
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ object SymDenotations {
888888
* As a side effect, drop Local flags of members that are not accessed via the ThisType
889889
* of their owner.
890890
*/
891-
final def isAccessibleFrom(pre: Type, superAccess: Boolean = false, whyNot: StringBuffer | Null = null)(using Context): Boolean = {
891+
final def isAccessibleFrom(pre: Type, superAccess: Boolean = false)(using Context): Boolean = {
892892

893893
/** Are we inside definition of `boundary`?
894894
* If this symbol is Java defined, package structure is interpreted to be flat.
@@ -905,26 +905,13 @@ object SymDenotations {
905905

906906
/** Is protected access to target symbol permitted? */
907907
def isProtectedAccessOK: Boolean =
908-
inline def fail(str: String): false =
909-
if whyNot != null then whyNot.nn.append(str)
910-
false
911908
val cls = owner.enclosingSubClass
912909
if !cls.exists then
913-
if pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) then
914-
true
915-
else
916-
val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass
917-
val location = if owner.is(Final) then owner.showLocated else owner.showLocated + " or one of its subclasses"
918-
fail(i"""
919-
| Protected $this can only be accessed from $location.""")
920-
else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then
910+
pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner)
911+
else
921912
// allow accesses to types from arbitrary subclasses fixes #4737
922913
// don't perform this check for static members
923-
true
924-
else
925-
val location = if cls.is(Final) then cls.showLocated else cls.showLocated + " or one of its subclasses"
926-
fail(i"""
927-
| Protected $this can only be accessed from $location.""")
914+
isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass)
928915
end isProtectedAccessOK
929916

930917
if pre eq NoPrefix then true

compiler/src/dotty/tools/dotc/reporting/messages.scala

+19-2
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ extends Message(PatternMatchExhaustivityID) {
876876

877877
val pathes = List(
878878
ActionPatch(
879-
srcPos = endPos,
879+
srcPos = endPos,
880880
replacement = uncoveredCases.map(c => indent(s"case $c => ???", startColumn))
881881
.mkString("\n", "\n", "")
882882
),
@@ -2986,7 +2986,24 @@ extends ReferenceMsg(CannotBeAccessedID):
29862986
i"none of the overloaded alternatives named $name can"
29872987
val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else ""
29882988
val whyNot = new StringBuffer
2989-
alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot))
2989+
for alt <- alts do
2990+
val cls = alt.owner.enclosingSubClass
2991+
val owner = if cls.exists then cls else alt.owner
2992+
val location: String =
2993+
if alt.is(Protected) then
2994+
if alt.privateWithin.exists && alt.privateWithin != owner then
2995+
if owner.is(Final) then alt.privateWithin.showLocated
2996+
else alt.privateWithin.showLocated + ", or " + owner.showLocated + " or one of its subclasses"
2997+
else
2998+
if owner.is(Final) then owner.showLocated
2999+
else owner.showLocated + " or one of its subclasses"
3000+
else
3001+
alt.privateWithin.orElse(owner).showLocated
3002+
val accessMod = if alt.is(Protected) then "protected" else "private"
3003+
val within = if alt.privateWithin.exists then i"[${alt.privateWithin.name}]"
3004+
else ""
3005+
whyNot.append(i"""
3006+
| $accessMod$within $alt can only be accessed from $location.""")
29903007
i"$whatCanNot be accessed as a member of $pre$where.$whyNot"
29913008
def explain(using Context) = ""
29923009

compiler/test-resources/repl/i1370

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ scala> object Lives { class Private { def foo1: Any = new Private.C1; def foo2:
33
1 | object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } }
44
| ^^^^^^^^^^
55
|constructor C1 cannot be accessed as a member of Lives.Private.C1 from class Private.
6+
| private constructor C1 can only be accessed from class C1 in object Private.
67
1 error found

tests/neg/i12573.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
|Extension methods were tried, but the search failed with:
66
|
77
| method getDFType cannot be accessed as a member of DFType.type from the top-level definitions in package <empty>.
8-
| Protected method getDFType can only be accessed from object DFType.
8+
| protected method getDFType can only be accessed from object DFType.

tests/neg/i14432c.check

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
12 |class Bar extends example.Foo(23) { // error: cant access private[example] ctor
33
| ^^^^^^^^^^^
44
| constructor Foo cannot be accessed as a member of example.Foo from class Bar.
5+
| private[example] constructor Foo can only be accessed from package example.
56
-- [E172] Type Error: tests/neg/i14432c.scala:16:43 --------------------------------------------------------------------
67
16 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror
78
| ^

tests/neg/i18686.check

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
-- [E173] Reference Error: tests/neg/i18686.scala:13:16 ----------------------------------------------------------------
2+
13 | println(Foo.Bar1) // error
3+
| ^^^^^^^^
4+
| value Bar1 cannot be accessed as a member of Foo.type from object Main.
5+
| private value Bar1 can only be accessed from object Foo.
6+
-- [E173] Reference Error: tests/neg/i18686.scala:14:16 ----------------------------------------------------------------
7+
14 | println(Foo.Bar2) // error
8+
| ^^^^^^^^
9+
| value Bar2 cannot be accessed as a member of Foo.type from object Main.
10+
| private[Foo] value Bar2 can only be accessed from object Foo.
11+
-- [E173] Reference Error: tests/neg/i18686.scala:15:16 ----------------------------------------------------------------
12+
15 | println(Foo.Bar3) // error
13+
| ^^^^^^^^
14+
| value Bar3 cannot be accessed as a member of Foo.type from object Main.
15+
| protected value Bar3 can only be accessed from object Foo.
16+
-- [E173] Reference Error: tests/neg/i18686.scala:16:16 ----------------------------------------------------------------
17+
16 | println(Foo.Bar4) // error
18+
| ^^^^^^^^
19+
| value Bar4 cannot be accessed as a member of Foo.type from object Main.
20+
| protected[Foo] value Bar4 can only be accessed from object Foo.
21+
-- [E173] Reference Error: tests/neg/i18686.scala:17:20 ----------------------------------------------------------------
22+
17 | println(Foo.Baz.Bar5) // error
23+
| ^^^^^^^^^^^^
24+
| value Bar5 cannot be accessed as a member of Foo.Baz.type from object Main.
25+
| private[Foo] value Bar5 can only be accessed from object Foo.
26+
-- [E173] Reference Error: tests/neg/i18686.scala:18:20 ----------------------------------------------------------------
27+
18 | println(Foo.Baz.Bar6) // error
28+
| ^^^^^^^^^^^^
29+
| value Bar6 cannot be accessed as a member of Foo.Baz.type from object Main.
30+
| protected[Foo] value Bar6 can only be accessed from object Foo.

tests/neg/i18686.scala

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object Foo:
2+
private val Bar1: Int = 1
3+
private[Foo] val Bar2: Int = 2
4+
protected val Bar3: Int = 3
5+
protected[Foo] val Bar4: Int = 5
6+
object Baz:
7+
private[Foo] val Bar5: Int = 5
8+
protected[Foo] val Bar6: Int = 6
9+
end Foo
10+
11+
object Main:
12+
def main(args: Array[String]): Unit =
13+
println(Foo.Bar1) // error
14+
println(Foo.Bar2) // error
15+
println(Foo.Bar3) // error
16+
println(Foo.Bar4) // error
17+
println(Foo.Baz.Bar5) // error
18+
println(Foo.Baz.Bar6) // error
19+
end main
20+
end Main

tests/neg/i18686b.check

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- [E173] Reference Error: tests/neg/i18686b.scala:15:16 ---------------------------------------------------------------
2+
15 | println(foo.Bar1) // error
3+
| ^^^^^^^^
4+
| value Bar1 cannot be accessed as a member of Foo from object Main.
5+
| private value Bar1 can only be accessed from class Foo.
6+
-- [E173] Reference Error: tests/neg/i18686b.scala:16:16 ---------------------------------------------------------------
7+
16 | println(foo.Bar2) // error
8+
| ^^^^^^^^
9+
| value Bar2 cannot be accessed as a member of Foo from object Main.
10+
| private[Foo] value Bar2 can only be accessed from class Foo.
11+
-- [E173] Reference Error: tests/neg/i18686b.scala:17:16 ---------------------------------------------------------------
12+
17 | println(foo.Bar3) // error
13+
| ^^^^^^^^
14+
| value Bar3 cannot be accessed as a member of Foo from object Main.
15+
| protected value Bar3 can only be accessed from class Foo or one of its subclasses.
16+
-- [E173] Reference Error: tests/neg/i18686b.scala:18:16 ---------------------------------------------------------------
17+
18 | println(foo.Bar4) // error
18+
| ^^^^^^^^
19+
| value Bar4 cannot be accessed as a member of Foo from object Main.
20+
| protected[Foo] value Bar4 can only be accessed from class Foo or one of its subclasses.
21+
-- [E008] Not Found Error: tests/neg/i18686b.scala:19:20 ---------------------------------------------------------------
22+
19 | println(foo.Baz.Bar5) // error
23+
| ^^^^^^^^^^^^
24+
| value Bar5 is not a member of object Foo#Baz
25+
-- [E008] Not Found Error: tests/neg/i18686b.scala:20:20 ---------------------------------------------------------------
26+
20 | println(foo.Baz.Bar6) // error
27+
| ^^^^^^^^^^^^
28+
| value Bar6 is not a member of object Foo#Baz

tests/neg/i18686b.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Foo:
2+
private val Bar1: Int = 1
3+
private[Foo] val Bar2: Int = 2
4+
protected val Bar3: Int = 3
5+
protected[Foo] val Bar4: Int = 5
6+
class Baz:
7+
private[Foo] val Bar5: Int = 5
8+
protected[Foo] val Bar6: Int = 6
9+
end Foo
10+
11+
def foo = new Foo
12+
13+
object Main:
14+
def main(args: Array[String]): Unit =
15+
println(foo.Bar1) // error
16+
println(foo.Bar2) // error
17+
println(foo.Bar3) // error
18+
println(foo.Bar4) // error
19+
println(foo.Baz.Bar5) // error
20+
println(foo.Baz.Bar6) // error
21+
end main
22+
end Main

tests/neg/i18686c.check

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- [E173] Reference Error: tests/neg/i18686c.scala:8:6 -----------------------------------------------------------------
2+
8 | foo.foo // error
3+
| ^^^^^^^
4+
|method foo cannot be accessed as a member of (foo² : Bar.Foo) from the top-level definitions in package <empty>.
5+
| protected[Bar] method foo can only be accessed from object Bar, or class Foo in object Bar or one of its subclasses.
6+
|
7+
|where: foo is a method in class Foo
8+
| foo² is a parameter in method test

tests/neg/i18686c.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Bar:
2+
class Foo:
3+
protected[Bar] def foo = 23
4+
class Qux extends Foo:
5+
val qux = foo
6+
7+
def test(foo: Bar.Foo) =
8+
foo.foo // error

tests/neg/i7709.check

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,39 @@
22
5 | class B extends X.Y // error
33
| ^^^
44
| class Y cannot be accessed as a member of X.type from class B.
5-
| Protected class Y can only be accessed from object X.
5+
| protected class Y can only be accessed from object X.
66
-- [E173] Reference Error: tests/neg/i7709.scala:6:21 ------------------------------------------------------------------
77
6 | class B2 extends X.Y: // error
88
| ^^^
99
| class Y cannot be accessed as a member of X.type from class B2.
10-
| Protected class Y can only be accessed from object X.
10+
| protected class Y can only be accessed from object X.
1111
-- [E173] Reference Error: tests/neg/i7709.scala:9:28 ------------------------------------------------------------------
1212
9 | class B4 extends B3(new X.Y) // error
1313
| ^^^
1414
| class Y cannot be accessed as a member of X.type from class B4.
15-
| Protected class Y can only be accessed from object X.
15+
| protected class Y can only be accessed from object X.
1616
-- [E173] Reference Error: tests/neg/i7709.scala:11:34 -----------------------------------------------------------------
1717
11 | def this(n: Int) = this(new X.Y().toString) // error
1818
| ^^^
1919
| class Y cannot be accessed as a member of X.type from class B5.
20-
| Protected class Y can only be accessed from object X.
20+
| protected class Y can only be accessed from object X.
2121
-- [E173] Reference Error: tests/neg/i7709.scala:13:20 -----------------------------------------------------------------
2222
13 | class B extends X.Y // error
2323
| ^^^
2424
| class Y cannot be accessed as a member of X.type from class B.
25-
| Protected class Y can only be accessed from object X.
25+
| protected class Y can only be accessed from object X.
2626
-- [E173] Reference Error: tests/neg/i7709.scala:18:18 -----------------------------------------------------------------
2727
18 | def y = new xx.Y // error
2828
| ^^^^
2929
| class Y cannot be accessed as a member of XX from class C.
30-
| Protected class Y can only be accessed from class XX or one of its subclasses.
30+
| protected class Y can only be accessed from class XX or one of its subclasses.
3131
-- [E173] Reference Error: tests/neg/i7709.scala:23:20 -----------------------------------------------------------------
3232
23 | def y = new xx.Y // error
3333
| ^^^^
3434
| class Y cannot be accessed as a member of XX from class D.
35-
| Protected class Y can only be accessed from class XX or one of its subclasses.
35+
| protected class Y can only be accessed from class XX or one of its subclasses.
3636
-- [E173] Reference Error: tests/neg/i7709.scala:31:20 -----------------------------------------------------------------
3737
31 | class Q extends X.Y // error
3838
| ^^^
3939
| class Y cannot be accessed as a member of p.X.type from class Q.
40-
| Protected class Y can only be accessed from object X in package p.
40+
| protected class Y can only be accessed from object X in package p.

tests/neg/not-accessible.check

+5
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
8 | def test(a: A) = a.x // error
33
| ^^^
44
| value x cannot be accessed as a member of (a : foo.A) from class B.
5+
| private[A] value x can only be accessed from class A in package foo.
56
-- [E173] Reference Error: tests/neg/not-accessible.scala:10:23 --------------------------------------------------------
67
10 | def test(a: A) = a.x // error
78
| ^^^
89
| value x cannot be accessed as a member of (a : foo.A) from object B.
10+
| private[A] value x can only be accessed from class A in package foo.
911
-- [E173] Reference Error: tests/neg/not-accessible.scala:13:23 --------------------------------------------------------
1012
13 | def test(a: A) = a.x // error
1113
| ^^^
1214
| value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package bar.
15+
| private[A] value x can only be accessed from class A in package foo.
1316
-- [E173] Reference Error: tests/neg/not-accessible.scala:5:21 ---------------------------------------------------------
1417
5 | def test(a: A) = a.x // error
1518
| ^^^
1619
| value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package foo.
20+
| private[A] value x can only be accessed from class A in package foo.
1721
-- [E173] Reference Error: tests/neg/not-accessible.scala:15:23 --------------------------------------------------------
1822
15 |def test(a: foo.A) = a.x // error
1923
| ^^^
2024
| value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package <empty>.
25+
| private[A] value x can only be accessed from class A in package foo.

0 commit comments

Comments
 (0)