Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 38cbd77

Browse files
authored
Merge pull request scala#11033 from som-snytt/issue/7415-similar-overloads
Lint dubious overload differs only in implicit [ci: last-only]
2 parents 9ad2167 + d856b48 commit 38cbd77

File tree

10 files changed

+222
-16
lines changed

10 files changed

+222
-16
lines changed

src/compiler/scala/tools/nsc/Reporting.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ object Reporting {
661661
LintIntDivToFloat,
662662
LintUniversalMethods,
663663
LintCloneable,
664+
LintOverload,
664665
LintNumericMethods
665666
= lint()
666667

src/compiler/scala/tools/nsc/settings/Warnings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ trait Warnings {
229229
val IntDivToFloat = LintWarning("int-div-to-float", "Warn when an integer division is converted (widened) to floating point: `(someInt / 2): Double`.")
230230
val PatternShadow = LintWarning("pattern-shadow", "Pattern variable id is also a term in scope.")
231231
val CloneableObject = LintWarning("cloneable", "Modules (objects) should not be Cloneable.")
232+
val DubiousOverload = LintWarning("overload", "Overload differs only in an implicit parameter.")
232233

233234
def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]]
234235
}
@@ -267,6 +268,7 @@ trait Warnings {
267268
def lintIntDivToFloat = lint.contains(IntDivToFloat)
268269
def warnPatternShadow = lint.contains(PatternShadow)
269270
def warnCloneableObject = lint.contains(CloneableObject)
271+
def warnDubiousOverload = lint.contains(DubiousOverload)
270272

271273
// The Xlint warning group.
272274
val lint = MultiChoiceSetting(

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

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.annotation._
1717
import scala.collection.mutable
1818
import scala.collection.mutable.ListBuffer
1919
import scala.reflect.internal.util.CodeAction
20-
import scala.tools.nsc.Reporting.WarningCategory
20+
import scala.tools.nsc.Reporting.WarningCategory, WarningCategory.{LintOverload}
2121
import scala.tools.nsc.settings.ScalaVersion
2222
import scala.tools.nsc.settings.NoScalaVersion
2323
import symtab.Flags._
@@ -160,6 +160,73 @@ abstract class RefChecks extends Transform {
160160
})
161161
}
162162
}
163+
private def checkDubiousOverloads(clazz: Symbol): Unit = if (settings.warnDubiousOverload) {
164+
// nullary members or methods with leading implicit params
165+
def ofInterest(tp: Type): Boolean = tp match {
166+
case mt: MethodType => mt.isImplicit
167+
case PolyType(_, rt) => ofInterest(rt)
168+
case _ => true // includes NullaryMethodType
169+
}
170+
// takes no value parameters
171+
def isNullary(tp: Type): Boolean = tp match {
172+
case _: MethodType => false
173+
case PolyType(_, rt) => isNullary(rt)
174+
case _ => true // includes NullaryMethodType
175+
}
176+
def warnDubious(sym: Symbol, alts: List[Symbol]): Unit = {
177+
val usage = if (sym.isMethod && !sym.isGetter) "Calls to parameterless" else "Usages of"
178+
val simpl = "a single implicit parameter list"
179+
val suffix = alts.filter(_ != sym).map(_.defString) match {
180+
case impl :: Nil => s"$impl, which has $simpl."
181+
case impls =>
182+
sm"""|overloads which have $simpl:
183+
| ${impls.mkString("\n ")}"""
184+
}
185+
val warnAt =
186+
if (sym.owner == clazz) sym.pos
187+
else
188+
alts.find(_.owner == clazz) match {
189+
case Some(conflict) => conflict.pos
190+
case _ => clazz.pos
191+
}
192+
refchecksWarning(warnAt, s"$usage $sym will be easy to mistake for calls to $suffix", LintOverload)
193+
}
194+
val byName =
195+
clazz.info.members
196+
.reverseIterator
197+
.filter(m => ofInterest(m.info))
198+
.toList
199+
.groupBy(_.name.dropLocal)
200+
def isCompetitive(syms: List[Symbol], sawNlly: Boolean, sawNonNlly: Boolean): Boolean =
201+
sawNlly && sawNonNlly || (syms match {
202+
case sym :: syms =>
203+
if (!sawNlly && isNullary(sym.info)) isCompetitive(syms, sawNlly = true, sawNonNlly)
204+
else if (!sawNonNlly && !isNullary(sym.info)) isCompetitive(syms, sawNlly, sawNonNlly = true)
205+
else isCompetitive(syms, sawNlly, sawNonNlly)
206+
case _ => false
207+
})
208+
for ((_, syms) <- byName if syms.lengthCompare(1) > 0 && isCompetitive(syms, sawNlly=false, sawNonNlly=false)) {
209+
val (nullaries, alts) = syms.partition(sym => isNullary(sym.info))
210+
//assert(!alts.isEmpty)
211+
nullaries match {
212+
case nullary :: Nil => warnDubious(nullary, syms)
213+
case nullaries =>
214+
//assert(!nullaries.isEmpty)
215+
val dealiased =
216+
nullaries.find(_.isPrivateLocal) match {
217+
case Some(local) =>
218+
nullaries.find(sym => sym.isAccessor && sym.accessed == local) match {
219+
case Some(accessor) => nullaries.filter(_ != local) // drop local if it has an accessor
220+
case _ => nullaries
221+
}
222+
case _ => nullaries
223+
}
224+
// there are multiple exactly for a private local and an inherited member
225+
for (nullary <- dealiased)
226+
warnDubious(nullary, nullary :: alts)
227+
}
228+
}
229+
}
163230

164231
// Override checking ------------------------------------------------------------
165232

@@ -1989,6 +2056,7 @@ abstract class RefChecks extends Transform {
19892056
checkOverloadedRestrictions(currentOwner, currentOwner)
19902057
// scala/bug#7870 default getters for constructors live in the companion module
19912058
checkOverloadedRestrictions(currentOwner, currentOwner.companionModule)
2059+
checkDubiousOverloads(currentOwner)
19922060
val bridges = addVarargBridges(currentOwner) // TODO: do this during uncurry?
19932061
checkAllOverrides(currentOwner)
19942062
checkAnyValSubclass(currentOwner)

src/library/scala/jdk/DoubleAccumulator.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import java.util.Spliterator
1717
import java.util.function.{Consumer, DoubleConsumer}
1818
import java.{lang => jl}
1919

20+
import scala.annotation._
2021
import scala.collection.Stepper.EfficientSplit
2122
import scala.collection.{AnyStepper, DoubleStepper, Factory, SeqFactory, Stepper, StepperShape, mutable}
2223
import scala.language.implicitConversions
@@ -236,6 +237,7 @@ final class DoubleAccumulator
236237
}
237238

238239
/** Copies the elements in this `DoubleAccumulator` into an `Array[Double]` */
240+
@nowarn // cat=lint-overload see toArray[B: ClassTag]
239241
def toArray: Array[Double] = {
240242
if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString)
241243
val a = new Array[Double](totalSize.toInt)

src/library/scala/jdk/IntAccumulator.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import java.util.Spliterator
1717
import java.util.function.{Consumer, IntConsumer}
1818
import java.{lang => jl}
1919

20+
import scala.annotation._
2021
import scala.collection.Stepper.EfficientSplit
2122
import scala.collection.{AnyStepper, Factory, IntStepper, SeqFactory, Stepper, StepperShape, mutable}
2223
import scala.language.implicitConversions
@@ -241,6 +242,7 @@ final class IntAccumulator
241242
}
242243

243244
/** Copies the elements in this `IntAccumulator` into an `Array[Int]` */
245+
@nowarn // cat=lint-overload see toArray[B: ClassTag]
244246
def toArray: Array[Int] = {
245247
if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString)
246248
val a = new Array[Int](totalSize.toInt)

src/library/scala/jdk/LongAccumulator.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import java.util.Spliterator
1717
import java.util.function.{Consumer, LongConsumer}
1818
import java.{lang => jl}
1919

20+
import scala.annotation._
2021
import scala.collection.Stepper.EfficientSplit
2122
import scala.collection.{AnyStepper, Factory, LongStepper, SeqFactory, Stepper, StepperShape, mutable}
2223
import scala.language.implicitConversions
@@ -236,6 +237,7 @@ final class LongAccumulator
236237
}
237238

238239
/** Copies the elements in this `LongAccumulator` into an `Array[Long]` */
240+
@nowarn // cat=lint-overload see toArray[B: ClassTag]
239241
def toArray: Array[Long] = {
240242
if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString)
241243
val a = new Array[Long](totalSize.toInt)

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ trait Types
977977
def load(sym: Symbol): Unit = {}
978978

979979
private def findDecl(name: Name, excludedFlags: Long): Symbol = {
980-
var alts: List[Symbol] = List()
980+
var alts: List[Symbol] = Nil
981981
var sym: Symbol = NoSymbol
982982
var e: ScopeEntry = decls.lookupEntry(name)
983983
while (e ne null) {
@@ -991,7 +991,7 @@ trait Types
991991
e = decls.lookupNextEntry(e)
992992
}
993993
if (alts.isEmpty) sym
994-
else (baseClasses.head.newOverloaded(this, alts))
994+
else baseClasses.head.newOverloaded(this, alts)
995995
}
996996

997997
/** Find all members meeting the flag requirements.
@@ -2930,8 +2930,10 @@ trait Types
29302930

29312931
object MethodType extends MethodTypeExtractor
29322932

2933-
// TODO: rename so it's more appropriate for the type that is for a method without argument lists
2934-
// ("nullary" erroneously implies it has an argument list with zero arguments, it actually has zero argument lists)
2933+
/** A method without parameter lists.
2934+
*
2935+
* Note: a MethodType with paramss that is a ListOfNil is called "nilary", to disambiguate.
2936+
*/
29352937
case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi {
29362938
override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations)
29372939
override def prefix: Type = resultType.prefix
@@ -2952,7 +2954,6 @@ trait Types
29522954
else NullaryMethodType(result1)
29532955
}
29542956
override def foldOver(folder: TypeFolder): Unit = folder(resultType)
2955-
29562957
}
29572958

29582959
object NullaryMethodType extends NullaryMethodTypeExtractor

test/files/neg/t7415.check

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
t7415.scala:10: warning: Calls to parameterless method foo will be easy to mistake for calls to def foo(implicit a: T): Int, which has a single implicit parameter list.
2+
def foo = 0 // warn
3+
^
4+
t7415.scala:14: warning: Usages of value foo will be easy to mistake for calls to def foo(implicit a: T): Int, which has a single implicit parameter list.
5+
val foo = 0 // warn
6+
^
7+
t7415.scala:18: warning: Usages of value foo will be easy to mistake for calls to def foo(implicit a: T): Int, which has a single implicit parameter list.
8+
private[this] val foo = 42 // warn
9+
^
10+
t7415.scala:31: warning: Calls to parameterless method foo will be easy to mistake for calls to def foo(implicit a: T): Int, which has a single implicit parameter list.
11+
class Mixed extends Base with T1 // warn here
12+
^
13+
t7415.scala:41: warning: Usages of value foo will be easy to mistake for calls to overloads which have a single implicit parameter list:
14+
def foo(implicit e: String): Int
15+
def foo(implicit e: Int): Int
16+
val foo = 0 // warn
17+
^
18+
t7415.scala:54: warning: Usages of value x will be easy to mistake for calls to def x(implicit t: T): Int, which has a single implicit parameter list.
19+
def x(implicit t: T) = 27 // warn
20+
^
21+
t7415.scala:65: warning: Usages of value i will be easy to mistake for calls to def i(implicit t: T): Int, which has a single implicit parameter list.
22+
class R(val i: Int) extends Q // warn
23+
^
24+
t7415.scala:66: warning: Usages of value i will be easy to mistake for calls to def i(implicit t: T): Int, which has a single implicit parameter list.
25+
class S(i: Int) extends R(i) { // warn
26+
^
27+
t7415.scala:66: warning: Usages of value i will be easy to mistake for calls to def i(implicit t: T): Int, which has a single implicit parameter list.
28+
class S(i: Int) extends R(i) { // warn
29+
^
30+
t7415.scala:76: warning: Calls to parameterless method f will be easy to mistake for calls to def f[A](implicit t: T): Int, which has a single implicit parameter list.
31+
def f[A] = 27 // warn
32+
^
33+
t7415.scala:82: warning: Calls to parameterless method foo will be easy to mistake for calls to def foo(implicit a: T): Int, which has a single implicit parameter list.
34+
val d1 = new Derived1 {} // warn
35+
^
36+
error: No warnings can be incurred under -Werror.
37+
11 warnings
38+
1 error

test/files/neg/t7415.scala

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//> using options -Werror -Xlint:overload
2+
3+
trait T
4+
5+
trait Base {
6+
def foo(implicit a: T) = 0
7+
}
8+
9+
trait Derived1 extends Base {
10+
def foo = 0 // warn
11+
}
12+
13+
trait Derived2 extends Base {
14+
val foo = 0 // warn
15+
}
16+
17+
class C extends Base {
18+
private[this] val foo = 42 // warn
19+
}
20+
21+
/* private local cannot directly conflict
22+
class C2 extends Derived2 {
23+
private[this] val foo = 42 // weaker access privileges in overriding
24+
}
25+
*/
26+
27+
trait T1 {
28+
def foo = 0
29+
}
30+
31+
class Mixed extends Base with T1 // warn here
32+
33+
class D {
34+
def foo(a: List[Int])(implicit d: DummyImplicit) = 0
35+
def foo(a: List[String]) = 1
36+
}
37+
38+
class CleverLukas {
39+
def foo(implicit e: String) = 1
40+
def foo(implicit e: Int) = 2
41+
val foo = 0 // warn
42+
}
43+
44+
class MoreInspiration {
45+
def foo(implicit a: T) = 0
46+
def foo() = 1 // has parens but Scala 2 allows `foo` with adaptation
47+
}
48+
49+
class X {
50+
val x = 42
51+
}
52+
53+
class Y extends X {
54+
def x(implicit t: T) = 27 // warn
55+
}
56+
57+
class J(val i: Int)
58+
class K(i: Int) extends J(i) { // no warn local i shadows member i that is not implicit method
59+
def f = i
60+
}
61+
62+
class Q {
63+
def i(implicit t: T) = 42
64+
}
65+
class R(val i: Int) extends Q // warn
66+
class S(i: Int) extends R(i) { // warn
67+
def f = i
68+
}
69+
70+
trait PBase {
71+
def f[A](implicit t: T) = 42
72+
def g[A](s: String) = s.toInt
73+
}
74+
75+
trait PDerived extends PBase {
76+
def f[A] = 27 // warn
77+
def g[A] = f[A] // no warn
78+
}
79+
80+
object Test extends App {
81+
implicit val t: T = new T {}
82+
val d1 = new Derived1 {} // warn
83+
println(d1.foo) // !
84+
val more = new MoreInspiration
85+
println(more.foo) // ?
86+
val y = new Y
87+
println(y.x) // you have been warned!
88+
}

0 commit comments

Comments
 (0)