Skip to content

Commit

Permalink
Warn if implicit should have explicit type
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Jul 22, 2022
1 parent 461d856 commit 5ff8926
Show file tree
Hide file tree
Showing 72 changed files with 422 additions and 63 deletions.
12 changes: 11 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Namers.scala
Expand Up @@ -1128,7 +1128,17 @@ trait Namers extends MethodSynthesis {
}
tree.tpt.defineType {
if (currentRun.isScala3.value && !pt.isWildcard && pt != NoType && !pt.isErroneous) pt
else dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt))
else {
val res = dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt))
if (tree.symbol.isImplicit && !tree.symbol.isLocalToBlock) {
val addendum = if (!res.isErroneous) s" (inferred $res)" else ""
if (currentRun.isScala3.value)
ErrorUtils.issueNormalTypeError(tree, s"Implicit definition must have explicit type$addendum")
else
context.warning(tree.pos, s"Implicit definition should have explicit type$addendum", WarningCategory.Other)
}
res
}
}.setPos(tree.pos.focus)
tree.tpt.tpe
}
Expand Down
3 changes: 2 additions & 1 deletion src/partest/scala/tools/partest/CompilerTest.scala
Expand Up @@ -12,6 +12,7 @@

package scala.tools.partest

import scala.language.implicitConversions
import scala.reflect.runtime.{universe => ru}
import scala.tools.nsc._

Expand Down Expand Up @@ -44,7 +45,7 @@ abstract class CompilerTest extends DirectTest {
if (sym eq NoSymbol) NoType
else appliedType(sym, compilerTypeFromTag(t))
}
implicit def mkMkType(sym: Symbol) = new MkType(sym)
implicit def mkMkType(sym: Symbol): MkType = new MkType(sym)

def allMembers(root: Symbol): List[Symbol] = {
def loop(seen: Set[Symbol], roots: List[Symbol]): List[Symbol] = {
Expand Down
2 changes: 1 addition & 1 deletion src/partest/scala/tools/partest/nest/Instance.scala
Expand Up @@ -28,5 +28,5 @@ trait Instance extends Spec {
def residualArgs = parsed.residualArgs // only args which were not options or args to options

type OptionMagic = Opt.Instance
protected implicit def optionMagicAdditions(name: String) = new Opt.Instance(programInfo, parsed, name)
protected implicit def optionMagicAdditions(name: String): Opt.Instance = new Opt.Instance(programInfo, parsed, name)
}
2 changes: 1 addition & 1 deletion src/partest/scala/tools/partest/nest/Reference.scala
Expand Up @@ -45,7 +45,7 @@ trait Reference extends Spec {
final def apply(args: String*): ThisCommandLine = creator(propertyArgs ++ args flatMap expandArg)

type OptionMagic = Opt.Reference
protected implicit def optionMagicAdditions(name: String) = new Opt.Reference(programInfo, options, name)
protected implicit def optionMagicAdditions(name: String): Opt.Reference = new Opt.Reference(programInfo, options, name)
}

object Reference {
Expand Down
3 changes: 1 addition & 2 deletions src/partest/scala/tools/partest/package.scala
Expand Up @@ -18,6 +18,7 @@ import java.util.concurrent.{Callable, ExecutorService}
import scala.concurrent.duration.Duration
import scala.io.Codec
import scala.jdk.CollectionConverters._
import scala.language.implicitConversions
import scala.tools.nsc.util.Exceptional

package object partest {
Expand Down Expand Up @@ -123,8 +124,6 @@ package object partest {
implicit def temporaryPath2File(x: Path): File = x.jfile
implicit def stringPathToJavaFile(path: String): File = new File(path)

implicit lazy val implicitConversions = scala.language.implicitConversions

def fileSeparator = java.io.File.separator
def pathSeparator = java.io.File.pathSeparator

Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/api/Internals.scala
Expand Up @@ -1098,7 +1098,7 @@ trait Internals { self: Universe =>
trait CompatApi {
/** @see [[CompatToken]] */
@deprecated("compatibility with Scala 2.10 EOL", "2.13.0")
implicit val token = new CompatToken
implicit val token: CompatToken = new CompatToken

/** Scala 2.10 compatibility enrichments for BuildApi. */
@deprecated("compatibility with Scala 2.10 EOL", "2.13.0")
Expand Down
2 changes: 1 addition & 1 deletion src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
Expand Up @@ -34,7 +34,7 @@ trait MemberHandlers {
val front = if (leadingPlus) "+ " else ""
front + (xs map string2codeQuoted mkString " + ")
}
private implicit def name2string(name: Name) = name.toString
private implicit def name2string(name: Name): String = name.toString

/** A traverser that finds all mentioned identifiers, i.e. things
* that need to be imported. It might return extra names.
Expand Down
4 changes: 2 additions & 2 deletions src/repl/scala/tools/nsc/interpreter/Power.scala
Expand Up @@ -264,7 +264,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re

trait Implicits1 {
// fallback
implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) =
implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]): PrettifierClass[T] =
new SinglePrettifierClass[T](x)
}
trait Implicits2 extends Implicits1 {
Expand All @@ -288,7 +288,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re
implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T]
implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym)

implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in)
implicit def replInputStream(in: InputStream)(implicit codec: Codec): RichInputStream = new RichInputStream(in)
implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec)
}

Expand Down
8 changes: 6 additions & 2 deletions src/repl/scala/tools/nsc/interpreter/ReplVals.scala
Expand Up @@ -47,13 +47,17 @@ class StdReplVals(final val intp: IMain) extends ReplVals {
import intp.global.Symbol

private val tagFn = ReplVals.mkCompilerTypeFromTag[intp.global.type](global)
implicit def mkCompilerTypeFromTag(sym: Symbol) = tagFn(sym)
implicit def mkCompilerTypeFromTag(sym: Symbol): ATFT[global.type] = tagFn(sym)
}

final lazy val replImplicits = new ReplImplicits

def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T]
}
trait ATFT[G <: Global] {
def apply[M](implicit m1: ru.TypeTag[M]): G#Type
def apply[M1, M2](implicit m1: ru.TypeTag[M1], m2: ru.TypeTag[M2]): G#Type
}

object ReplVals {
/** Latest attempt to work around the challenge of foo.global.Type
Expand All @@ -72,7 +76,7 @@ object ReplVals {
def compilerTypeFromTag(t: ApiUniverse # WeakTypeTag[_]): Global#Type =
definitions.compilerTypeFromTag(t)

class AppliedTypeFromTags(sym: Symbol) {
class AppliedTypeFromTags(sym: Symbol) extends ATFT[T] {
def apply[M](implicit m1: ru.TypeTag[M]): Type =
if (sym eq NoSymbol) NoType
else appliedType(sym, compilerTypeFromTag(m1).asInstanceOf[Type])
Expand Down
2 changes: 1 addition & 1 deletion src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala
Expand Up @@ -29,7 +29,7 @@ trait Uncompilable {
import global.definitions.{ AnyRefClass, AnyRefTpe }
import global.rootMirror.RootClass

private implicit def translateName(name: Global#Name) =
private implicit def translateName(name: Global#Name): global.Name =
if (name.isTypeName) newTypeName("" + name) else newTermName("" + name)

val WaitNames = List("scala", "AnyRef", "wait")
Expand Down
Expand Up @@ -114,7 +114,7 @@ case class ScalaSig(majorVersion: Int, minorVersion: Int, table: Seq[Int ~ ByteC

def parseEntry(index: Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index))

implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(parser)(this)
implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]): A = ScalaSigParsers.expect(parser)(this)

override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + {
for (i <- 0 until table.size) yield "" + i + ":\t" + parseEntry(i) // + "\n\t" + getEntry(i)
Expand Down Expand Up @@ -165,7 +165,8 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules {

def parseEntry[A](parser: EntryParser[A])(index: Int) = (toEntry(index) -~ parser)

implicit def entryType(code: Int) = key filter (_ == code)
type R = scala.tools.scalap.scalax.rules.Rule[ScalaSigEntryParsers.S, ScalaSigEntryParsers.S, Int, Nothing]
implicit def entryType(code: Int): R = key.filter(_ == code)

val index = read(_.index)
val key = read(_.entryType)
Expand Down
2 changes: 1 addition & 1 deletion test/async/jvm/toughtype.scala
Expand Up @@ -199,7 +199,7 @@ package scala.async.run.toughtype {
}

object FunDep {
implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]) =
implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]): FunDep[W,List[S],W] =
new FunDep[W, List[S], W] {
def method(w: W, l: List[S]) = async {
val it = l.iterator
Expand Down
14 changes: 9 additions & 5 deletions test/files/jvm/future-spec/main.scala
Expand Up @@ -17,9 +17,10 @@ object Test {
}

trait Features {
implicit def implicitously = scala.language.implicitConversions
implicit def reflectively = scala.language.reflectiveCalls
implicit def postulously = scala.language.postfixOps
import languageFeature._
implicit def implicitously: implicitConversions = scala.language.implicitConversions
implicit def reflectively: reflectiveCalls = scala.language.reflectiveCalls
implicit def postulously: postfixOps = scala.language.postfixOps
}


Expand All @@ -40,7 +41,9 @@ trait MinimalScalaTest extends Output with Features with Vigil {
if (throwables.nonEmpty) println(buffer.toString)
}

implicit def stringops(s: String) = new {
type Ops = AnyRef{def should[U](snippets: => U): U; def in[U](snippet: => U): scala.collection.mutable.IndexedSeq[_ >: Char with Throwable] with scala.collection.mutable.AbstractSeq[_ >: Char with Throwable] with scala.collection.mutable.Growable[Char with Throwable] with java.io.Serializable}

implicit def stringops(s: String): Ops = new {

def should[U](snippets: => U) = {
bufferPrintln(s + " should:")
Expand All @@ -62,7 +65,8 @@ trait MinimalScalaTest extends Output with Features with Vigil {

}

implicit def objectops(obj: Any) = new {
type OOps = AnyRef{def mustBe(other: Any): Unit; def mustEqual(other: Any): Unit}
implicit def objectops(obj: Any): OOps = new {

def mustBe(other: Any) = assert(obj == other, s"$obj is not $other")
def mustEqual(other: Any) = mustBe(other)
Expand Down
2 changes: 2 additions & 0 deletions test/files/jvm/interpreter.check
Expand Up @@ -87,6 +87,8 @@ scala> case class Bar(n: Int)
class Bar

scala> implicit def foo2bar(foo: Foo) = Bar(foo.n)
^
warning: Implicit definition should have explicit type (inferred Bar)
warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature`
def foo2bar(foo: Foo): Bar

Expand Down
8 changes: 4 additions & 4 deletions test/files/neg/implicit-log.scala
Expand Up @@ -37,8 +37,8 @@ object Test1579 {
class Query[E](val value: E)
class Invoker(q: Any) { val foo = null }

implicit def unwrap[C](q: Query[C]) = q.value
implicit def invoker(q: Query[Column]) = new Invoker(q)
implicit def unwrap[C](q: Query[C]): C = q.value
implicit def invoker(q: Query[Column]): Invoker = new Invoker(q)

val q = new Query(new Column)
q.foo
Expand All @@ -50,9 +50,9 @@ object Test1625 {
def unwrap() = x
}

implicit def byName[A](x: => A) = new Wrapped(x)
implicit def byName[A](x: => A): Wrapped = new Wrapped(x)

implicit def byVal[A](x: A) = x
implicit def byVal[A](x: A): A = x

def main(args: Array[String]) = {

Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/implicits.check
Expand Up @@ -16,4 +16,11 @@ implicits.scala:47: error: type mismatch;
implicits.scala:59: error: could not find implicit value for parameter x: Nothing
foo {
^
implicits.scala:34: warning: Implicit definition should have explicit type (inferred T)
implicit def select[T](t: HSome[T,_]) = t.head
^
implicits.scala:35: warning: Implicit definition should have explicit type (inferred L)
implicit def selectTail[L](t: HSome[_,L]) = t.tail
^
2 warnings
4 errors
4 changes: 4 additions & 0 deletions test/files/neg/override-final-implicit.check
@@ -1,5 +1,9 @@
override-final-implicit.scala:6: warning: Implicit definition should have explicit type (inferred Test.this.FooExtender)
override implicit def FooExtender(foo: String) = super.FooExtender(foo)
^
override-final-implicit.scala:6: error: cannot override final member:
final implicit def FooExtender(foo: String): Test.this.FooExtender (defined in class Implicits)
override implicit def FooExtender(foo: String) = super.FooExtender(foo)
^
1 warning
1 error
4 changes: 4 additions & 0 deletions test/files/neg/private-implicit-class.check
@@ -1,4 +1,8 @@
private-implicit-class.scala:6: error: method BarExtender in class ImplicitsPrivate cannot be accessed as a member of ImplicitsPrivate from class TestPrivate
override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error
^
private-implicit-class.scala:6: warning: Implicit definition should have explicit type
override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error
^
1 warning
1 error
4 changes: 4 additions & 0 deletions test/files/neg/t2206.check
Expand Up @@ -2,4 +2,8 @@ t2206.scala:10: error: value f is not a member of o.A
Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type
a.f()
^
t2206.scala:13: warning: Implicit definition should have explicit type (inferred o.AX)
implicit def ax(a: A) = new AX
^
1 warning
1 error
4 changes: 4 additions & 0 deletions test/files/neg/t2421b.check
@@ -1,4 +1,8 @@
t2421b.scala:12: error: could not find implicit value for parameter aa: Test.F[Test.A]
f
^
t2421b.scala:10: warning: Implicit definition should have explicit type (inferred Test.F[X])
implicit def b[X <: B] = new F[X]()
^
1 warning
1 error
4 changes: 4 additions & 0 deletions test/files/neg/t3006.check
Expand Up @@ -3,4 +3,8 @@ t3006.scala:8: error: type mismatch;
required: Int
println(A(3) + "H")
^
t3006.scala:6: warning: Implicit definition should have explicit type (inferred Test.Foo)
implicit def aToFoo(x: A) = new Foo(x);
^
1 warning
1 error
4 changes: 4 additions & 0 deletions test/files/neg/t3346c.check
Expand Up @@ -2,4 +2,8 @@ t3346c.scala:60: error: value bar is not a member of Either[Int,String]
did you mean map?
eii.bar
^
t3346c.scala:35: warning: Implicit definition should have explicit type (inferred Test.TCValue[M,A]{implicit val M: Test.TC[M]; val self: M[A]})
implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] {
^
1 warning
1 error
16 changes: 16 additions & 0 deletions test/files/neg/t3346i.check
Expand Up @@ -4,4 +4,20 @@ t3346i.scala:28: error: value a is not a member of Test.A[T]
t3346i.scala:29: error: value a is not a member of Test.A[Nothing]
(new A[Nothing]).a
^
t3346i.scala:16: warning: Implicit definition should have explicit type (inferred Implicit1[T])
implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b)
^
t3346i.scala:18: warning: Implicit definition should have explicit type (inferred Implicit2[T])
implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
^
t3346i.scala:19: warning: Implicit definition should have explicit type (inferred Implicit2[T])
implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c)
^
t3346i.scala:21: warning: Implicit definition should have explicit type (inferred Implicit3[T])
implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]()
^
t3346i.scala:22: warning: Implicit definition should have explicit type (inferred Implicit3[T])
implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]()
^
5 warnings
2 errors
19 changes: 19 additions & 0 deletions test/files/neg/t4271.check
Expand Up @@ -8,4 +8,23 @@ t4271.scala:11: error: value -> is not a member of Int
did you mean >>>?
3 -> 5
^
t4271.scala:3: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def Ensuring[A](x: A) = Donotuseme
^
t4271.scala:4: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def doubleWrapper(x: Int) = Donotuseme
^
t4271.scala:5: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def floatWrapper(x: Int) = Donotuseme
^
t4271.scala:6: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def intWrapper(x: Int) = Donotuseme
^
t4271.scala:7: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def longWrapper(x: Int) = Donotuseme
^
t4271.scala:8: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type)
implicit def ArrowAssoc[A](x: A) = Donotuseme
^
6 warnings
3 errors
16 changes: 16 additions & 0 deletions test/files/neg/t4457_1.check
Expand Up @@ -4,4 +4,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm
match argument types (Float)
val x = aFunc(4F)
^
t4457_1.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float])
implicit def conv1(i: Float) = new NE[Float]
^
t4457_1.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException])
implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException]
^
t4457_1.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float])
implicit def conv4(op: AA[Float]) = new N[Float]
^
t4457_1.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float])
implicit def conv7(i: Float) = new NZ[Float]
^
t4457_1.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar])
implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar]
^
5 warnings
1 error
16 changes: 16 additions & 0 deletions test/files/neg/t4457_2.check
Expand Up @@ -10,4 +10,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm
match argument types (Float)
bFunc(aFunc(4F))
^
t4457_2.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float])
implicit def conv1(i: Float) = new NE[Float]
^
t4457_2.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException])
implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException]
^
t4457_2.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float])
implicit def conv4(op: AA[Float]) = new N[Float]
^
t4457_2.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float])
implicit def conv7(i: Float) = new NZ[Float]
^
t4457_2.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar])
implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar]
^
5 warnings
2 errors
4 changes: 4 additions & 0 deletions test/files/neg/t4568.check
@@ -1,4 +1,8 @@
t4568.scala:8: error: recursive method isSubListOf needs result type
case h :: t => y.contains(h) && (t.isSubListOf(y.drop(y.indexOf(h) + 1)))
^
t4568.scala:2: warning: Implicit definition should have explicit type (inferred SubList.SubListable[A])
implicit def sublistable[A](x: List[A]) = new SubListable(x)
^
1 warning
1 error

0 comments on commit 5ff8926

Please sign in to comment.