Warn for code that depend on subtle semantics of class parameters in constructors
See original GitHub issueMinimized example
class B(val y: Int):
println(y) // A warning is due here
foo()
def foo() = println(y)
class C(override val y: Int) extends B(10)
@main
def Test = new C(20)
Output
10
20
Expectation
The code above depends on subtle semantics of class parameters in constructors, for which the programmer should receive a warning.
As discussed in https://github.com/lampepfl/dotty/issues/15723#issuecomment-1193181067, one possibility to issue the warning is:
Rule A: Issue a warning for the usage of an unqualified class parameter x
in the constructor (including usage in super constructor calls) if this.x
is overridden in a subclass.
This rule, however, is too coarse-grained, as programmers will get an annoying warning for the following code:
class B(val y: Int):
println(y) // should not issue a warning here
foo()
def foo() = println(y)
class C(override val y: Int) extends B(y)
Here is a better rule to avoid the spurious warnings in the above:
Rule B: Issue a warning for the usage of an unqualified class parameter x
in the constructor (including usage in super constructor calls) if its semantics deviate from that of this.x
.
The plan is to implement the rule in the initialization checker. Implementation of Rule B would require extending the abstract domain with a symbolic value in the spirit of symbolic execution.
Issue Analytics
- State:
- Created a year ago
- Comments:10 (7 by maintainers)
Top GitHub Comments
The guarantee is the following:
We have conducted an empirical study both for Dotty and community projects, we find no valid use which violates the rules/guarantees above.
For a new language design, it seems good to simply make class parameters final – which we cannot do in Scala because of legacy code. Therefore, we propose the check as an alternative.
The new check is actually very simple. We only reason about the symbolic value for the overridding parameter is passed to the overridden parameter.
For that purpose, the checker only checks super constructor calls,
x
andthis.a
, all other expressions are ignored. It’s guaranteed to be fast and always terminates.Found more instances of overridding class parameters in #15815 .
The following two are in Scaladoc:
The following one comes from munit:
Fastparse:
Effpi:
Akka-actor (there are many such code):
endpoints4s:
specs2:
stdlib:
jackson-module-scala:
scala-parser-combinator:
sconfig: