Skip to content

[REVISIT] Warn when arms of if or match lub to Any #10479

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ trait Infer extends Checkable {
if (!result && !seen(t)) t.dealiasWidenChain.foreach(saw)
}
}
@`inline` def containsAny(t: Type) = collector.collect(t)
@inline def containsAny(t: Type): Boolean = collector.collect(t)
val hasAny = containsAny(pt) || containsAny(restpe) ||
formals.exists(containsAny) ||
argtpes.exists(containsAny) ||
Expand Down
15 changes: 11 additions & 4 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}

private val topTypes: List[Symbol] = List(AnyClass, AnyValClass, ObjectClass)
private def checkedLub(tree: Tree, res: Type): res.type = {
if (settings.warnInferAny && topTypes.contains(res.typeSymbol))
context.warning(tree.pos, s"a type was inferred to be `${res.typeSymbol.name}`; this may indicate a programming error.", WarningCategory.LintInferAny)
res
}

def reenterValueParams(vparamss: List[List[ValDef]]): Unit = {
for (vparams <- vparamss)
for (vparam <- vparams)
Expand Down Expand Up @@ -2696,9 +2703,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (isFullyDefined(pt))
finish(casesTyped, pt)
else packedTypes(casesTyped) match {
case packed if sameWeakLubAsLub(packed) => finish(casesTyped, lub(packed))
case packed if sameWeakLubAsLub(packed) => finish(casesTyped, checkedLub(tree, lub(packed)))
case packed =>
val lub = weakLub(packed)
val lub = checkedLub(tree, weakLub(packed))
finish(casesTyped map (adaptCase(_, mode, lub)), lub)
}
}
Expand Down Expand Up @@ -4811,9 +4818,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// Important to deconst, otherwise `if (???) 0 else 0` evaluates to 0 (scala/bug#6331)
else thenp1.tpe.deconst :: elsep1.tpe.deconst :: Nil match {
case tp :: _ if samePackedTypes => finish(tp)
case tpes if sameWeakLubAsLub(tpes) => finish(lub(tpes))
case tpes if sameWeakLubAsLub(tpes) => finish(checkedLub(tree, lub(tpes)))
case tpes =>
val lub = weakLub(tpes)
val lub = checkedLub(tree, weakLub(tpes))
treeCopy.If(tree, cond1, adapt(thenp1, mode, lub), adapt(elsep1, mode, lub)) setType lub
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,14 @@ private[internal] trait GlbLubs {

/** Does this set of types have the same weak lub as
* it does regular lub? This is exposed so lub callers
* can discover whether the trees they are typing will
* can discover whether the trees they are typing
* may require further adaptation. It may return false
* negatives, but it will not return false positives.
*/
def sameWeakLubAsLub(tps: List[Type]) = tps match {
case Nil => true
case tp :: Nil => tp.annotations.isEmpty
case tps => tps.forall(_.annotations.isEmpty) && !(tps forall isNumericValueType)
case tps => tps.forall(_.annotations.isEmpty) && !tps.forall(isNumericValueType)
}

/** If the arguments are all numeric value types, the numeric
Expand Down
15 changes: 15 additions & 0 deletions test/files/neg/t12829.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
t12829.scala:6: warning: a type was inferred to be `Any`; this may indicate a programming error.
def f(i: Int, t: Boolean) = if (t) b += i else 42
^
t12829.scala:8: warning: a type was inferred to be `Any`; this may indicate a programming error.
def h(i: Int, t: Boolean) = g(if (t) b += i)
^
t12829.scala:10: warning: a type was inferred to be `Any`; this may indicate a programming error.
t match {
^
t12829.scala:14: warning: a type was inferred to be `Any`; this may indicate a programming error.
def z = List(42, "hello")
^
error: No warnings can be incurred under -Werror.
4 warnings
1 error
15 changes: 15 additions & 0 deletions test/files/neg/t12829.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

// scalac: -Werror -Xlint:infer-any

class C {
val b = collection.mutable.ListBuffer.empty[Int]
def f(i: Int, t: Boolean) = if (t) b += i else 42
def g[A](a: A): A = a
def h(i: Int, t: Boolean) = g(if (t) b += i)
def n(i: Int, t: Boolean) =
t match {
case true => b += i
case _ => 42
}
def z = List(42, "hello")
}