Skip to content

Commit 95c8a40

Browse files
committed
Revert to unused implicit check, also synthetics
- Check implicit and given imports - Adapt check to some synthetic (@main, package object) - Add new tests to support these features
1 parent d41c6dd commit 95c8a40

File tree

3 files changed

+113
-67
lines changed

3 files changed

+113
-67
lines changed

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import dotty.tools.dotc.ast.untpd.ImportSelector
77
import dotty.tools.dotc.config.ScalaSettings
88
import dotty.tools.dotc.core.Contexts.*
99
import dotty.tools.dotc.core.Decorators.{em, i}
10-
import dotty.tools.dotc.core.Flags.{Given, GivenVal, Param, Private, SelfName}
10+
import dotty.tools.dotc.core.Flags.{Given, Implicit, GivenOrImplicit, Param, Private, SelfName, Synthetic}
1111
import dotty.tools.dotc.core.Phases.Phase
1212
import dotty.tools.dotc.core.StdNames
1313
import dotty.tools.dotc.report
@@ -182,27 +182,25 @@ object CheckUnused:
182182

183183
/** Register a found (used) symbol */
184184
def registerUsed(sym: Symbol)(using Context): Unit =
185-
usedInScope.top += sym -> sym.isAccessibleAsIdent
186-
usedDef += sym
185+
if !isConstructorOfSynth(sym) then
186+
usedInScope.top += sym -> sym.isAccessibleAsIdent
187+
usedDef += sym
187188

188189
/** Register a list of found (used) symbols */
189190
def registerUsed(syms: Seq[Symbol])(using Context): Unit =
190-
usedInScope.top ++= syms.map(s => s -> s.isAccessibleAsIdent)
191+
usedInScope.top ++= syms.filterNot(isConstructorOfSynth).map(s => s -> s.isAccessibleAsIdent)
191192
usedDef ++= syms
192193

193194
/** Register an import */
194195
def registerImport(imp: tpd.Import)(using Context): Unit =
195-
val tpd.Import(qual, selectors) = imp
196-
if !tpd.languageImport(qual).nonEmpty then
196+
if !tpd.languageImport(imp.expr).nonEmpty then
197197
impInScope.top += imp
198-
unusedImport ++= selectors.filter{ s =>
199-
!isImportExclusion(s) && !s.isGiven && !isSelectorOnAGiven(qual, s)
200-
}
198+
unusedImport ++= imp.selectors.filter(s => !isImportExclusion(s))
201199

202200
/** Register (or not) some `val` or `def` according to the context, scope and flags */
203201
def registerDef(valOrDef: tpd.ValOrDefDef)(using Context): Unit =
204-
if valOrDef.symbol.is(Param) then
205-
if valOrDef.symbol.is(Given) then
202+
if valOrDef.symbol.is(Param) && !isSyntheticMainParam(valOrDef.symbol) then
203+
if valOrDef.symbol.isOneOf(GivenOrImplicit) then
206204
implicitParamInScope += valOrDef
207205
else
208206
explicitParamInScope += valOrDef
@@ -296,8 +294,33 @@ object CheckUnused:
296294
end getUnused
297295
//============================ HELPERS ====================================
298296

299-
private def isSelectorOnAGiven(qual: tpd.Tree, sel: ImportSelector)(using Context): Boolean =
300-
qual.tpe.member(sel.name).alternatives.exists(_.symbol.is(Given))
297+
/**
298+
* Is the the constructor of synthetic package object
299+
* Should be ignored as it is always imported/used in package
300+
* Trigger false negative on used import
301+
*
302+
* Without this check example:
303+
*
304+
* --- WITH PACKAGE : WRONG ---
305+
* {{{
306+
* package a:
307+
* val x: Int = 0
308+
* package b:
309+
* import a._ // no warning
310+
* }}}
311+
* --- WITH OBJECT : OK ---
312+
* {{{
313+
* object a:
314+
* val x: Int = 0
315+
* object b:
316+
* import a._ // unused warning
317+
* }}}
318+
*/
319+
private def isConstructorOfSynth(sym: Symbol)(using Context): Boolean =
320+
sym.exists && sym.isConstructor && sym.owner.isPackageObject && sym.owner.is(Synthetic)
321+
322+
private def isSyntheticMainParam(sym: Symbol)(using Context): Boolean =
323+
sym.exists && ctx.platform.isMainMethod(sym.owner) && sym.owner.is(Synthetic)
301324

302325
private def isImportExclusion(sel: ImportSelector): Boolean = sel.renamed match
303326
case untpd.Ident(name) => name == StdNames.nme.WILDCARD
@@ -319,7 +342,7 @@ object CheckUnused:
319342
val tpd.Import(qual, sels) = imp
320343
val qualHasSymbol = qual.tpe.member(sym.name).symbol == sym
321344
def selector = sels.find(sel => sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name)
322-
def wildcard = sels.find(sel => sel.isWildcard && (sym.is(Given) == sel.isGiven))
345+
def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit)))
323346
if qualHasSymbol && !isAccessible then
324347
selector.orElse(wildcard) // selector with name or wildcard (or given)
325348
else

tests/neg-custom-args/fatal-warnings/i15503a.scala

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ object FooNested:
3131
object Nested:
3232
def hello = Set()
3333

34+
object FooGivenUnused:
35+
import SomeGivenImports.given // error
36+
37+
object FooGiven:
38+
import SomeGivenImports.given // OK
39+
import SomeGivenImports._ // error
40+
41+
val foo = summon[Int]
3442

3543
/**
3644
* Import used as type name are considered
@@ -84,27 +92,43 @@ object IgnoreExclusion:
8492
def check =
8593
val a = Set(1)
8694
val b = Map(1 -> 2)
95+
/**
96+
* Some given values for the test
97+
*/
98+
object SomeGivenImports:
99+
given Int = 0
100+
given String = "foo"
87101

88102
/* BEGIN : Check on packages*/
89-
package p {
90-
class C
91-
}
92-
93-
package p {
94-
import p._ // error
95-
package q {
96-
class U {
97-
def f = new C
103+
package testsamepackageimport:
104+
package p {
105+
class C
106+
}
107+
108+
package p {
109+
import p._ // error
110+
package q {
111+
class U {
112+
def f = new C
113+
}
98114
}
99115
}
100-
}
116+
// -----------------------
117+
118+
package testpackageimport:
119+
package a:
120+
val x: Int = 0
121+
122+
package b:
123+
import a._ // error
124+
125+
101126
/* END : Check on packages*/
102127

103128
/* BEGIN : tests on meta-language features */
104-
object TestLanguageImportAreIgnored:
129+
object TestGivenCoversionScala2:
105130
/* note: scala3 Conversion[U,T] do not require an import */
106131
import language.implicitConversions // OK
107-
import language._ // OK
108132

109133
implicit def doubleToInt(d:Double):Int = d.toInt
110134

@@ -126,7 +150,7 @@ object GivenImportOrderAtoB:
126150
object B { implicit val y: Y = new Y }
127151
class C {
128152
import A._ // error
129-
import B._
153+
import B._ // OK
130154
def t = implicitly[X]
131155
}
132156

@@ -136,47 +160,31 @@ object GivenImportOrderBtoA:
136160
object A { implicit val x: X = new X }
137161
object B { implicit val y: Y = new Y }
138162
class C {
139-
import B._
163+
import B._ // OK
140164
import A._ // error
141165
def t = implicitly[X]
142166
}
143167
/* END : tests on given import order */
144168

145-
/*
146-
* Advanced tests on given imports meta-programming
147-
*
148-
* - Currently also tests that no imported implicits are reported
149-
*/
150-
151-
package summoninlineconflict:
152-
package lib:
153-
trait A
154-
trait B
155-
trait C
156-
trait X
157-
158-
given willBeUnused: (A & X) = new A with X {}
159-
given willBeUsed: (A & B) = new A with B {}
160-
given notUsedAtAll: Int = 0
161-
162-
package use:
163-
import lib.{A, B, C, willBeUnused, willBeUsed, notUsedAtAll} // OK
164-
import compiletime.summonInline // OK
165-
166-
transparent inline given conflictInside: C =
167-
summonInline[A]
168-
new {}
169-
170-
transparent inline given potentialConflict: C =
171-
summonInline[B]
172-
new {}
173-
174-
val b: B = summon[B]
175-
val c: C = summon[C]
176-
177-
package unusedgivensimports:
178-
package foo:
179-
given Int = 0
180-
181-
package bar:
182-
import foo.given // OK
169+
/* Scala 2 implicits */
170+
object Scala2ImplicitsGiven:
171+
object A:
172+
implicit val x: Int = 1
173+
object B:
174+
import A.given // OK
175+
val b = summon[Int]
176+
object C:
177+
import A.given // error
178+
val b = 1
179+
object D:
180+
import A._ // OK
181+
val b = summon[Int]
182+
object E:
183+
import A._ // error
184+
val b = 1
185+
object F:
186+
import A.x // OK
187+
val b = summon[Int]
188+
object G:
189+
import A.x // error
190+
val b = 1

tests/neg-custom-args/fatal-warnings/i15503e.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,19 @@ def f2(a: Int) = 1 // error
55
def f3(a: Int)(using Int) = a // OK
66
def f4(a: Int)(using Int) = 1 // error
77
def f6(a: Int)(using Int) = summon[Int] // error
8-
def f7(a: Int)(using Int) = summon[Int] + a // OK
8+
def f7(a: Int)(using Int) = summon[Int] + a // OK
9+
10+
package scala2main:
11+
object happyBirthday {
12+
def main(args: Array[String]): Unit = ??? // error
13+
}
14+
15+
package scala2mainunused:
16+
object happyBirthday {
17+
def main(args: Array[String]): Unit = // OK
18+
val a = args.size
19+
???
20+
}
21+
22+
package scala3main:
23+
@main def hello = ??? // OK

0 commit comments

Comments
 (0)