Skip to content

Commit

Permalink
Don't ignore params of public methods
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Nov 5, 2024
1 parent 1d4db7f commit 882cb84
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 15 deletions.
7 changes: 3 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
override def prepareForDefDef(tree: DefDef)(using Context): Context =
preparing:
ud.registerTrivial(tree)
if !tree.symbol.is(Private) then
tree.termParamss.flatten.foreach(p => ud.addIgnoredParam(p.symbol))
ud.addIgnoredUsage(tree.symbol)

override def transformDefDef(tree: DefDef)(using Context): tree.type =
Expand Down Expand Up @@ -571,10 +569,11 @@ object CheckUnused:
private def isConstructorOfSynth(sym: Symbol)(using Context): Boolean =
sym.exists && sym.isConstructor && sym.owner.isPackageObject && sym.owner.is(Synthetic)

/** This is used to avoid reporting the parameters of the synthetic main method generated by `@main`.
/** Avoid reporting unused parameter in required main method signature.
* The method may be user-written or generated by `@main`.
*/
private def isSyntheticMainParam(sym: Symbol)(using Context): Boolean =
sym.exists && ctx.platform.isMainMethod(sym.owner) && sym.owner.is(Synthetic)
sym.exists && ctx.platform.isMainMethod(sym.owner) //&& sym.owner.is(Synthetic)

/** If -Wunused:strict-no-implicit-warn import and this import selector could potentially import implicit.
*/
Expand Down
6 changes: 3 additions & 3 deletions tests/warn/i15503e.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//> using options -Wunused:explicits
//> using options -Wunused:explicits

object Foo {
/* This goes around the "trivial method" detection */
Expand Down Expand Up @@ -31,7 +31,7 @@ package scala3main:
package foo.test.lambda.param:
val default_val = 1
val a = (i: Int) => i // OK
val b = (i: Int) => default_val // OK
val b = (i: Int) => default_val // warn
val c = (_: Int) => default_val // OK

package foo.test.trivial:
Expand Down Expand Up @@ -67,4 +67,4 @@ package foo.test.i16865:
def fn(a: Int, b: Int): Int = b + 3 // OK

object Ex2 extends Bar:
override def fn(a: Int, b: Int): Int = b + 3 // OK
override def fn(a: Int, b: Int): Int = b + 3 // OK
8 changes: 4 additions & 4 deletions tests/warn/i15503h.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//> using options -Wunused:linted
//> using options -Wunused:linted

import collection.mutable.Set // warn

Expand All @@ -7,14 +7,14 @@ class A {
val b = 2 // OK

private def c = 2 // warn
def d(using x:Int): Int = b // ok
def d(using x: Int): Int = b // warn
def e(x: Int) = 1 // OK
def f =
val x = 1 // warn
def f = 2 // warn
3

def g(x: Int): Int = x match
case x:1 => 0 // OK
case x: 1 => 0 // OK
case _ => 1
}
}
10 changes: 6 additions & 4 deletions tests/warn/i15503i.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ class A {
private def c2 = 2 // OK
def c3 = c2

def d1(using x:Int): Int = default_int // ok
def d2(using x:Int): Int = x // OK
def d1(using x: Int): Int = default_int // warn param
def d2(using x: Int): Int = x // OK
def d3(using Int): Int = summon[Int] // OK
def d4(using Int): Int = default_int // warn

def e1(x: Int) = default_int // ok
def e1(x: Int) = default_int // warn param
def e2(x: Int) = x // OK
def f =
val x = 1 // warn
Expand All @@ -44,7 +46,7 @@ package foo.test.scala.annotation:
val default_int = 12

def a1(a: Int) = a // OK
def a2(a: Int) = default_int // ok
def a2(a: Int) = default_int // warn

def a3(@unused a: Int) = default_int //OK

Expand Down
43 changes: 43 additions & 0 deletions tests/warn/unused-locals.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//> using options -Wunused:locals

class Outer {
class Inner
}

trait Locals {
def f0 = {
var x = 1 // warn
var y = 2 // no warn
y = 3
y + y
}
def f1 = {
val a = new Outer // no warn
val b = new Outer // warn
new a.Inner
}
def f2 = {
var x = 100 // warn about it being a var
x
}
}

object Types {
def l1() = {
object HiObject { def f = this } // warn
class Hi { // warn
def f1: Hi = new Hi
def f2(x: Hi) = x
}
class DingDongDoobie // warn
class Bippy // no warn
type Something = Bippy // no warn
type OtherThing = String // warn
(new Bippy): Something
}
}

// breakage: local val x$1 in method skolemize is never used
case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) {
def skolemize: SymbolKind = copy(accurate = s"$accurate skolem", abbreviation = s"$abbreviation#SKO")
}
153 changes: 153 additions & 0 deletions tests/warn/unused-params.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//> using options -Wunused:params -Werror
//

import Answers._

trait InterFace {
/** Call something. */
def call(a: Int, b: String, c: Double): Int
}

trait BadAPI extends InterFace {
def f(a: Int,
b: String, // warn
c: Double): Int = {
println(c)
a
}
@deprecated("no warn in deprecated API", since="yesterday")
def g(a: Int,
b: String, // no warn
c: Double): Int = {
println(c)
a
}
override def call(a: Int,
b: String, // no warn, required by superclass
c: Double): Int = {
println(c)
a
}

def meth(x: Int) = x

override def equals(other: Any): Boolean = true // no warn

def i(implicit s: String) = answer // yes, warn

/*
def future(x: Int): Int = {
val y = 42
val x = y // maybe option to warn only if shadowed
x
}
*/
}

// mustn't alter warnings in super
trait PoorClient extends BadAPI {
override def meth(x: Int) = ??? // no warn
override def f(a: Int, b: String, c: Double): Int = a + b.toInt + c.toInt
}

class Unusing(u: Int) { // warn
def f = ???
}

class Valuing(val u: Int) // no warn

class Revaluing(u: Int) { def f = u } // no warn

case class CaseyKasem(k: Int) // no warn

case class CaseyAtTheBat(k: Int)(s: String) // warn

trait Ignorance {
def f(readResolve: Int) = answer // warn
}

class Reusing(u: Int) extends Unusing(u) // no warn

class Main {
def main(args: Array[String]): Unit = println("hello, args") // no warn
}

trait Unimplementation {
def f(u: Int): Int = ??? // no warn for param in unimplementation
}

trait DumbStuff {
def f(implicit dummy: DummyImplicit) = answer
def g(dummy: DummyImplicit) = answer
}
trait Proofs {
def f[A, B](implicit ev: A =:= B) = answer
def g[A, B](implicit ev: A <:< B) = answer
def f2[A, B](ev: A =:= B) = answer
def g2[A, B](ev: A <:< B) = answer
}

trait Anonymous {
def f = (i: Int) => answer // warn

def f1 = (_: Int) => answer // no warn underscore parameter (a fresh name)

def f2: Int => Int = _ + 1 // no warn placeholder syntax (a fresh name and synthetic parameter)

def g = for (i <- List(1)) yield answer // no warn patvar elaborated as map.(i => 42)
}
trait Context[A] { def m(a: A): A = a }
trait Implicits {
def f[A](implicit ctx: Context[A]) = answer
def g[A: Context] = answer
}
class Bound[A: Context]
object Answers {
def answer: Int = 42
}

trait BadMix { _: InterFace =>
def f(a: Int,
b: String, // warn
c: Double): Int = {
println(c)
a
}
@deprecated("no warn in deprecated API", since="yesterday")
def g(a: Int,
b: String, // no warn
c: Double): Int = {
println(c)
a
}
override def call(a: Int,
b: String, // no warn, required by superclass
c: Double): Int = {
println(c)
a
}

def meth(x: Int) = x

override def equals(other: Any): Boolean = true // no warn

def i(implicit s: String) = answer // yes, warn
}

class Unequal {
override def equals(other: Any) = toString.nonEmpty // no warn non-trivial RHS, required by universal method
}

class Seriously {
def f(s: Serializable) = toString.nonEmpty // warn explicit param of marker trait
}

class TryStart(start: String) {
def FINALLY(end: END.type) = start
}

object END

class Nested {
@annotation.unused private def actuallyNotUsed(fresh: Int, stale: Int) = fresh
}

0 comments on commit 882cb84

Please sign in to comment.