@@ -6,6 +6,7 @@ import interfaces.CompilerCallback
6
6
import Decorators ._
7
7
import Periods ._
8
8
import Names ._
9
+ import Flags .*
9
10
import Phases ._
10
11
import Types ._
11
12
import Symbols ._
@@ -20,14 +21,17 @@ import Nullables._
20
21
import Implicits .ContextualImplicits
21
22
import config .Settings ._
22
23
import config .Config
24
+ import config .SourceVersion .allSourceVersionNames
23
25
import reporting ._
24
26
import io .{AbstractFile , NoAbstractFile , PlainFile , Path }
25
27
import scala .io .Codec
26
28
import collection .mutable
27
29
import printing ._
28
- import config .{JavaPlatform , SJSPlatform , Platform , ScalaSettings }
30
+ import config .{JavaPlatform , SJSPlatform , Platform , ScalaSettings , ScalaRelease }
29
31
import classfile .ReusableDataReader
30
32
import StdNames .nme
33
+ import parsing .Parsers .EnclosingSpan
34
+ import util .Spans .NoSpan
31
35
32
36
import scala .annotation .internal .sharable
33
37
@@ -40,7 +44,9 @@ import plugins._
40
44
import java .util .concurrent .atomic .AtomicInteger
41
45
import java .nio .file .InvalidPathException
42
46
43
- object Contexts {
47
+ import scala .util .chaining .given
48
+
49
+ object Contexts :
44
50
45
51
private val (compilerCallbackLoc, store1) = Store .empty.newLocation[CompilerCallback ]()
46
52
private val (sbtCallbackLoc, store2) = store1.newLocation[AnalysisCallback ]()
@@ -52,8 +58,9 @@ object Contexts {
52
58
private val (notNullInfosLoc, store8) = store7.newLocation[List [NotNullInfo ]]()
53
59
private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null ]()
54
60
private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner ](TypeAssigner )
61
+ private val (usagesLoc, store11) = store10.newLocation[Usages ]()
55
62
56
- private val initialStore = store10
63
+ private val initialStore = store11
57
64
58
65
/** The current context */
59
66
inline def ctx (using ctx : Context ): Context = ctx
@@ -239,6 +246,9 @@ object Contexts {
239
246
/** The current type assigner or typer */
240
247
def typeAssigner : TypeAssigner = store(typeAssignerLoc)
241
248
249
+ /** Tracker for usages of elements such as import selectors. */
250
+ def usages : Usages = store(usagesLoc)
251
+
242
252
/** The new implicit references that are introduced by this scope */
243
253
protected var implicitsCache : ContextualImplicits | Null = null
244
254
def implicits : ContextualImplicits = {
@@ -247,9 +257,7 @@ object Contexts {
247
257
val implicitRefs : List [ImplicitRef ] =
248
258
if (isClassDefContext)
249
259
try owner.thisType.implicitMembers
250
- catch {
251
- case ex : CyclicReference => Nil
252
- }
260
+ catch case ex : CyclicReference => Nil
253
261
else if (isImportContext) importInfo.nn.importedImplicits
254
262
else if (isNonEmptyScopeContext) scope.implicitDecls
255
263
else Nil
@@ -475,8 +483,8 @@ object Contexts {
475
483
else fresh.setOwner(exprOwner)
476
484
477
485
/** A new context that summarizes an import statement */
478
- def importContext (imp : Import [? ], sym : Symbol ): FreshContext =
479
- fresh.setImportInfo(ImportInfo (sym, imp.selectors, imp.expr))
486
+ def importContext (imp : Import [? ], sym : Symbol , enteringSyms : Boolean = false ): FreshContext =
487
+ fresh.setImportInfo(ImportInfo (sym, imp.selectors, imp.expr, imp.attachmentOrElse( EnclosingSpan , NoSpan )).tap(importInfo => if enteringSyms && ctx.settings. WunusedHas .imports then usages += importInfo ))
480
488
481
489
/** Is the debug option set? */
482
490
def debug : Boolean = base.settings.Ydebug .value
@@ -812,6 +820,7 @@ object Contexts {
812
820
store = initialStore
813
821
.updated(settingsStateLoc, settingsGroup.defaultState)
814
822
.updated(notNullInfosLoc, Nil )
823
+ .updated(usagesLoc, Usages ())
815
824
.updated(compilationUnitLoc, NoCompilationUnit )
816
825
searchHistory = new SearchRoot
817
826
gadt = EmptyGadtConstraint
@@ -939,7 +948,7 @@ object Contexts {
939
948
private [dotc] var stopInlining : Boolean = false
940
949
941
950
/** A variable that records that some error was reported in a globally committable context.
942
- * The error will not necessarlily be emitted, since it could still be that
951
+ * The error will not necessarily be emitted, since it could still be that
943
952
* the enclosing context will be aborted. The variable is used as a smoke test
944
953
* to turn off assertions that might be wrong if the program is erroneous. To
945
954
* just test for `ctx.reporter.errorsReported` is not always enough, since it
@@ -996,4 +1005,49 @@ object Contexts {
996
1005
if (thread == null ) thread = Thread .currentThread()
997
1006
else assert(thread == Thread .currentThread(), " illegal multithreaded access to ContextBase" )
998
1007
}
999
- }
1008
+ end ContextState
1009
+
1010
+ /** Collect information about the run for purposes of additional diagnostics.
1011
+ */
1012
+ class Usages :
1013
+ private val selectors = mutable.Map .empty[ImportInfo , Set [untpd.ImportSelector ]].withDefaultValue(Set .empty)
1014
+ private val importInfos = mutable.Map .empty[CompilationUnit , List [(ImportInfo , Symbol )]].withDefaultValue(Nil )
1015
+
1016
+ // register an import
1017
+ def += (info : ImportInfo )(using Context ): Unit =
1018
+ def isLanguageImport = info.isLanguageImport && allSourceVersionNames.exists(info.forwardMapping.contains)
1019
+ if ctx.settings.WunusedHas .imports && ! isLanguageImport && ! ctx.owner.is(Enum ) && ! ctx.compilationUnit.isJava then
1020
+ importInfos(ctx.compilationUnit) ::= ((info, ctx.owner))
1021
+
1022
+ // mark a selector as used
1023
+ def use (info : ImportInfo , selector : untpd.ImportSelector )(using Context ): Unit =
1024
+ if ctx.settings.WunusedHas .imports && ! info.isRootImport then
1025
+ selectors(info) += selector
1026
+
1027
+ // unused import, owner, which selector
1028
+ def unused (using Context ): List [(ImportInfo , Symbol , List [untpd.ImportSelector ])] =
1029
+ if ctx.settings.WunusedHas .imports && ! ctx.compilationUnit.isJava then
1030
+ var unusages = List .empty[(ImportInfo , Symbol , List [untpd.ImportSelector ])]
1031
+ def checkUsed (info : ImportInfo , owner : Symbol ): Unit =
1032
+ val usedSelectors = selectors.remove(info).getOrElse(Set .empty)
1033
+ var unusedSelectors = List .empty[untpd.ImportSelector ]
1034
+ def cull (toCheck : List [untpd.ImportSelector ]): Unit =
1035
+ toCheck match
1036
+ case selector :: rest =>
1037
+ cull(rest) // reverse
1038
+ if ! selector.isMask && ! usedSelectors(selector) then
1039
+ unusedSelectors ::= selector
1040
+ case _ =>
1041
+ cull(info.selectors)
1042
+ if unusedSelectors.nonEmpty then unusages ::= (info, owner, unusedSelectors)
1043
+ end checkUsed
1044
+ importInfos.remove(ctx.compilationUnit).foreach(_.foreach(checkUsed))
1045
+ unusages
1046
+ else
1047
+ Nil
1048
+ end unused
1049
+
1050
+ def clear ()(using Context ): Unit =
1051
+ importInfos.clear()
1052
+ selectors.clear()
1053
+ end Usages
0 commit comments