Skip to content

Commit c7ec038

Browse files
committed
Warn about surprising shadowing.
It's hidden behind -Xlint and pretty specific, but makes me feel better anyway. References SI-4762, no review.
1 parent 3a195c7 commit c7ec038

File tree

5 files changed

+86
-6
lines changed

5 files changed

+86
-6
lines changed

src/compiler/scala/reflect/internal/Symbols.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,6 +1923,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
19231923
else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE
19241924
else ExplicitFlags
19251925

1926+
def accessString = hasFlagsToString(PRIVATE | PROTECTED | LOCAL)
19261927
def defaultFlagString = hasFlagsToString(defaultFlagMask)
19271928
private def defStringCompose(infoString: String) = compose(
19281929
defaultFlagString,

src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,33 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
191191
mayNeedProtectedAccessor(sel, args, false)
192192

193193
case sel @ Select(qual @ This(_), name) =>
194-
// direct calls to aliases of param accessors to the superclass in order to avoid
195-
// duplicating fields.
196-
if (sym.isParamAccessor && sym.alias != NoSymbol) {
194+
// warn if they are selecting a private[this] member which
195+
// also exists in a superclass, because they may be surprised
196+
// to find out that a constructor parameter will shadow a
197+
// field. See SI-4762.
198+
if (settings.lint.value) {
199+
if (sym.isPrivateLocal && sym.paramss.isEmpty) {
200+
qual.symbol.ancestors foreach { parent =>
201+
parent.info.decls filterNot (x => x.isPrivate || x.hasLocalFlag) foreach { m2 =>
202+
if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) {
203+
unit.warning(sel.pos,
204+
sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name
205+
+ " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within "
206+
+ sym.owner + " - you may want to give them distinct names."
207+
)
208+
}
209+
}
210+
}
211+
}
212+
}
213+
214+
// direct calls to aliases of param accessors to the superclass in order to avoid
215+
// duplicating fields.
216+
if (sym.isParamAccessor && sym.alias != NoSymbol) {
197217
val result = localTyper.typed {
198-
Select(
199-
Super(qual, tpnme.EMPTY/*qual.symbol.info.parents.head.symbol.name*/) setPos qual.pos,
200-
sym.alias) setPos tree.pos
218+
Select(
219+
Super(qual, tpnme.EMPTY/*qual.symbol.info.parents.head.symbol.name*/) setPos qual.pos,
220+
sym.alias) setPos tree.pos
201221
}
202222
debuglog("alias replacement: " + tree + " ==> " + result);//debug
203223
localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true))

test/files/neg/t4762.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t4762.scala:15: error: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B - you may want to give them distinct names.
2+
/* (99,99) */ (this.x, this.y),
3+
^
4+
t4762.scala:48: error: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived - you may want to give them distinct names.
5+
class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }
6+
^
7+
two errors found

test/files/neg/t4762.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xlint -Xfatal-warnings

test/files/neg/t4762.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// https://issues.scala-lang.org/browse/SI-4762
2+
3+
// In A, x and y are -1.
4+
class A(var x: Int) {
5+
val y: Int = -1
6+
}
7+
8+
// In B, x and y are 99 and private[this], implicitly so
9+
// in the case of x.
10+
class B(x: Int) extends A(-1) {
11+
private[this] def y: Int = 99
12+
13+
// Three distinct results.
14+
def f = List(
15+
/* (99,99) */ (this.x, this.y),
16+
/* (-1,99) */ ((this: B).x, (this: B).y),
17+
/* (-1,-1) */ ((this: A).x, (this: A).y)
18+
)
19+
20+
// The 99s tell us we are reading the private[this]
21+
// data of a different instance.
22+
def g(b: B) = List(
23+
/* (-1,99) */ (b.x, b.y),
24+
/* (-1,99) */ ((b: B).x, (b: B).y),
25+
/* (-1,-1) */ ((b: A).x, (b: A).y)
26+
)
27+
}
28+
29+
object Test {
30+
def f(x: A) = /* -2 */ x.x + x.y
31+
def g1(x: B) = /* -2 */ (x: A).x + (x: A).y
32+
def g2(x: B) = (x: B).x + (x: B).y
33+
// java.lang.IllegalAccessError: tried to access method B.y()I from class Test$
34+
35+
def main(args: Array[String]): Unit = {
36+
val b = new B(99)
37+
b.f foreach println
38+
b.g(new B(99)) foreach println
39+
40+
println(f(b))
41+
println(g1(b))
42+
println(g2(b))
43+
}
44+
}
45+
46+
class bug4762 {
47+
class Base( var x : Int ) { def increment() { x = x + 1 } }
48+
class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }
49+
50+
val derived = new Derived( 1 )
51+
}

0 commit comments

Comments
 (0)