@@ -7,24 +7,18 @@ import dotty.tools.dotc.ast.tpd.*
77import dotty .tools .dotc .ast .untpd , untpd .ImportSelector
88import dotty .tools .dotc .config .ScalaSettings
99import dotty .tools .dotc .core .Contexts .*
10- import dotty .tools .dotc .core .Decorators .{em , i , toMessage }
11- import dotty .tools .dotc .core .Denotations .SingleDenotation
1210import dotty .tools .dotc .core .Flags .*
13- import dotty .tools .dotc .core .Phases .Phase
1411import dotty .tools .dotc .core .StdNames .nme
15- import dotty .tools .dotc .core .Types .{AnnotatedType , ClassInfo , ConstantType , NamedType , NoPrefix , NoType , TermRef , Type , TypeProxy , TypeTraverser }
16- import dotty .tools .dotc .core .Flags
17- import dotty .tools .dotc .core .Names .{SimpleName , DerivedName }
18- import dotty .tools .dotc .core .Names .{Name , TermName , termName }
12+ import dotty .tools .dotc .core .Types .*
13+ import dotty .tools .dotc .core .Names .{Name , SimpleName , DerivedName }
1914import dotty .tools .dotc .core .NameOps .isReplWrapperName
2015import dotty .tools .dotc .core .NameKinds .{ContextFunctionParamName , WildcardParamName }
2116import dotty .tools .dotc .core .Symbols .{NoSymbol , Symbol , defn , isDeprecated }
2217import dotty .tools .dotc .report
23- import dotty .tools .dotc .reporting .{ Message , UnusedSymbol as UnusedSymbolMessage }
18+ import dotty .tools .dotc .reporting .UnusedSymbol
2419import dotty .tools .dotc .transform .MegaPhase .MiniPhase
2520import dotty .tools .dotc .typer .ImportInfo
2621import dotty .tools .dotc .util .{Property , SrcPos }
27- import dotty .tools .dotc .util .Spans .Span
2822import dotty .tools .dotc .util .chaining .*
2923
3024import scala .collection .mutable , mutable .{ArrayBuilder , ListBuffer , Stack }
@@ -50,16 +44,23 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
5044 val infos = tree.getAttachment(refInfosKey).getOrElse(RefInfos ())
5145 ctx.fresh.setProperty(refInfosKey, infos).tap(_ => tree.putAttachment(refInfosKey, infos))
5246 override def transformUnit (tree : Tree )(using Context ): tree.type =
47+ /*
48+ val checkAnnots = new TreeTraverser:
49+ def traverse(tree: Tree)(using Context) = tree match
50+ case tree =>
51+ if tree.symbol.exists then
52+ println(s"${tree.symbol} ${tree.symbol.id} -> ${tree.symbol.annotations}")
53+ traverseChildren(tree)
54+ checkAnnots.traverse(tree)
55+ */
5356 if phaseMode == PhaseMode .Report then
5457 reportUnused()
5558 tree
5659
5760 override def transformIdent (tree : Ident )(using Context ): tree.type =
5861 if tree.symbol.exists then
59- println(s " ID SYM ${tree.name} ${tree.symbol} in ${tree.typeOpt.importPrefix.skipPackageObject}" )
6062 resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
6163 else if tree.hasType then
62- println(s " ID TYP ${tree.name} ${tree.tpe.classSymbol} in ${tree.tpe.importPrefix.skipPackageObject}" )
6364 resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
6465 tree
6566
@@ -83,10 +84,32 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
8384 tree
8485
8586 override def prepareForValDef (tree : ValDef )(using Context ): Context =
87+ /*
88+ // selftype of object is not a usage
89+ val moduleSelfRef = ctx.owner.is(Module) && ctx.owner == tree.symbol.companionModule.moduleClass
90+ if !moduleSelfRef then
91+
92+ if !tree.symbol.is(Module) then // do not register the ValDef generated for `object`
93+ if ctx.owner.is(Module) then //&& tree.symbol.is(SelfName) then
94+ println(s"AHA selfie $tree")
95+ tree.tpt match
96+ case SingletonTypeTree(ref) => ctx.owner == ref.symbol.companionModule.moduleClass
97+ case _ =>
98+ */
8699 refInfos.register(tree)
87100 ctx
88101 override def transformValDef (tree : ValDef )(using Context ): tree.type =
89102 traverseAnnotations(tree.symbol)
103+ if tree.name.startsWith(" derived$" ) && tree.hasType then
104+ def loop (t : Tree ): Unit = t match
105+ case Ident (name) =>
106+ val target =
107+ val ts0 = t.tpe.typeSymbol
108+ if ts0.is(ModuleClass ) then ts0.companionModule else ts0
109+ resolveUsage(target, name, t.tpe.underlyingPrefix.skipPackageObject)
110+ case Select (t, _) => loop(t)
111+ case _ =>
112+ tree.getAttachment(OriginalTypeClass ).foreach(loop)
90113 tree
91114
92115 override def prepareForDefDef (tree : DefDef )(using Context ): Context =
@@ -147,7 +170,10 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
147170 tparams.foreach(transformAllDeep)
148171 transformAllDeep(body)
149172 case SingletonTypeTree (ref) =>
150- transformAllDeep(ref)
173+ // selftype of object is not a usage
174+ val moduleSelfRef = ctx.owner.is(Module ) && ctx.owner == tree.symbol.companionModule.moduleClass
175+ if ! moduleSelfRef then
176+ transformAllDeep(ref)
151177 case TypeBoundsTree (lo, hi, alias) =>
152178 transformAllDeep(lo)
153179 transformAllDeep(hi)
@@ -206,21 +232,18 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
206232 }
207233 if matches then sel else loop(sels)
208234 case sel :: sels =>
209- def allSymbols = (
210- qtpe.member(sel.name).alternatives
211- ::: qtpe.member(sel.name.toTypeName).alternatives
212- ).map(_.symbol).toSet
235+ def hasAltMember (nm : Name ) = qtpe.member(nm).hasAltWith(_.symbol == sym)
213236 // if there is an explicit name, it must match
214237 val matches = ! name.exists(_.toTermName != sel.rename) &&
215- (prefix.eq(NoPrefix ) || qtpe =:= prefix) && allSymbols.contains(sym )
238+ (prefix.eq(NoPrefix ) || qtpe =:= prefix) && (hasAltMember(sel.name) || hasAltMember(sel.name.toTypeName) )
216239 if matches then sel else loop(sels)
217240 case nil => null
218241 loop(info.selectors)
219242
220243 def checkMember (ctxsym : Symbol ): Boolean =
221244 ctxsym.isClass && sym.owner.isClass
222245 && ctxsym.thisType.baseClasses.contains(sym.owner)
223- && ctxsym.thisType.member(sym.name).alternatives.exists(_ .containsSym(sym))
246+ && ctxsym.thisType.member(sym.name).hasAltWith(d => d .containsSym(sym) && ! name.exists(_ != d.name ))
224247
225248 def addCached (where : Context ): Unit =
226249 where.property(resolvedKey) match
@@ -285,14 +308,13 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
285308 end while
286309 // println(s"RESULT encl $foundEnclosing isLoc $isLocal cand $candidate imp $importer")
287310 if foundEnclosing then
288- println( s " detected enclosing $sym " )
311+ ( )
289312 else if isLocal then
290313 refInfos.refs.addOne(sym)
291314 else if candidate != NoContext then
292315 refInfos.refs.addOne(sym)
293- if candidate.isImportContext then
294- if importer != null then
295- refInfos.sels.addOne(importer)
316+ if candidate.isImportContext && importer != null then
317+ refInfos.sels.addOne(importer)
296318 // addCached(candidate)
297319 end resolveUsage
298320
@@ -304,16 +326,6 @@ object CheckUnused:
304326 case Aggregate
305327 case Report
306328
307- private enum WarnTypes :
308- case Imports
309- case LocalDefs
310- case ExplicitParams
311- case ImplicitParams
312- case PrivateMembers
313- case PatVars
314- case UnsetLocals
315- case UnsetPrivates
316-
317329 val refInfosKey = Property .StickyKey [RefInfos ]
318330
319331 val resolvedKey = Property .Key [Resolved ]
@@ -347,6 +359,7 @@ object CheckUnused:
347359 case imp : Import =>
348360 if languageImport(imp.expr).isEmpty
349361 && ! imp.isGeneratedByEnum
362+ && ! imp.isTransparentInline
350363 then
351364 imps.addOne(imp)
352365 case tree : NamedDefTree =>
@@ -361,81 +374,81 @@ object CheckUnused:
361374 val seen = mutable.Map .empty[Symbol , List [(Name , Type )]].withDefaultValue(Nil )
362375
363376 def reportUnused ()(using Context ): Unit =
364- /*
365- case PatVars => patVars
366- */
367- val warnings = ArrayBuilder .make[(UnusedSymbolMessage , SrcPos )]
377+ val warnings = ArrayBuilder .make[(UnusedSymbol , SrcPos )]
368378 val infos = refInfos
369- for (sym, pos) <- infos.defs.iterator if ! sym.hasAnnotation(ctx.definitions .UnusedAnnot ) do
379+ for (sym, pos) <- infos.defs.iterator if ! sym.hasAnnotation(defn .UnusedAnnot ) do
370380 if infos.refs(sym) then
371381 if sym.isLocalToBlock then
372382 if ctx.settings.WunusedHas .locals && sym.is(Mutable ) && ! infos.asss(sym) then
373- warnings.addOne((UnusedSymbolMessage .unsetLocals, pos))
383+ warnings.addOne((UnusedSymbol .unsetLocals, pos))
374384 else if sym.isAllOf(Private | Mutable ) && ! infos.asss(sym) then
375- warnings.addOne((UnusedSymbolMessage .unsetPrivates, pos))
385+ warnings.addOne((UnusedSymbol .unsetPrivates, pos))
376386 else if sym.is(Private , butNot = ParamAccessor ) then
377387 if ctx.settings.WunusedHas .privates
378388 && ! sym.isConstructor
379389 && sym.is(Private , butNot = SelfName | Synthetic | CaseAccessor )
380390 then
381- warnings.addOne((UnusedSymbolMessage .privateMembers, pos))
391+ warnings.addOne((UnusedSymbol .privateMembers, pos))
382392 else if sym.is(Param , butNot = Given | Implicit ) then
383393 val m = sym.owner
384- if ctx.settings.WunusedHas .explicits
385- && ! infos.skip(m)
386- && ! sym.isAllOf(AccessorCreationFlags )
387- // don't warn for class param with accessor alias
388- // && !(sym.owner.isPrimaryConstructor && sym.owner.owner.info.nonPrivateMember(sym.name).exists)
389- && {
390- // in constructors, class param is finally assigned to field.
391- // For now, look for a like-named accessor that is
392- // - nonprivate or
393- // - is a case element which may be pattern matched
394- // - or is itself used
395- val isAliasedClassParam = m.isPrimaryConstructor && {
396- // val a = m.owner.info.nonPrivateMember(sym.name)
397- val a = m.owner.info.member(sym.name)
398- a.exists && {
399- val asym = a.symbol
400- val res = asym.is(ParamAccessor ) && (! asym.is(Private ) || asym.is(CaseAccessor ) || infos.refs(asym))
401- // println(s"CHK($res) $asym ${ asym.is(ParamAccessor)} ${ asym.is(Private)} ${ asym.is(CaseAccessor)} ${ infos.refs(asym)}")
402- asym.is(ParamAccessor ) && (! asym.is(Private ) || asym.is(CaseAccessor ) || infos.refs(asym))
403- }
404- }
405- ! isAliasedClassParam
406- }
407- && {
408- val isExtendedReceiver = sym.owner.is(ExtensionMethod ) && {
409- val ps = m.paramSymss.dropWhile(_.exists(_.isTypeParam))
410- ps match
411- case (h :: Nil ) :: Nil => h == sym
394+ def checkExplicit (): Unit =
395+ // A class param is unused if its param accessor is unused.
396+ // (The class param is not assigned to a field until constructors.)
397+ // A local param accessor warns as a param; a private accessor as a private member.
398+ // Avoid warning for case class elements because they are aliased via unapply.
399+ if m.isPrimaryConstructor then
400+ val alias = m.owner.info.member(sym.name)
401+ if alias.exists then
402+ val aliasSym = alias.symbol
403+ if aliasSym.isAllOf(PrivateParamAccessor , butNot = CaseAccessor ) && ! infos.refs(alias.symbol) then
404+ if aliasSym.is(Local ) then
405+ if ctx.settings.WunusedHas .explicits then
406+ warnings.addOne((UnusedSymbol .explicitParams, pos))
407+ else
408+ if ctx.settings.WunusedHas .privates then
409+ warnings.addOne((UnusedSymbol .privateMembers, pos))
410+ else if ctx.settings.WunusedHas .explicits
411+ && ! sym.is(Synthetic ) // param to setter is unused bc there is no field yet
412+ && ! (sym.owner.is(ExtensionMethod ) && {
413+ m.paramSymss.dropWhile(_.exists(_.isTypeParam)) match
414+ case (h :: Nil ) :: Nil => h == sym // param is the extended receiver
412415 case _ => false
416+ })
417+ && ! sym.name.isInstanceOf [DerivedName ]
418+ /*
419+ && {
420+ sym.name match
421+ //case n: SimpleName => !n.contains('$')
422+ case n: DerivedName => false
423+ case _ => true
413424 }
414- ! isExtendedReceiver
415- }
416- && ! sym.name.isInstanceOf [DerivedName ]
417- /*
418- && {
419- sym.name match
420- //case n: SimpleName => !n.contains('$')
421- case n: DerivedName => false
422- case _ => true
423- }
424- */
425- && ! ctx.platform.isMainMethod(m)
426- then
427- warnings.addOne((UnusedSymbolMessage .explicitParams, pos))
425+ */
426+ && ! ctx.platform.isMainMethod(m)
427+ then
428+ warnings.addOne((UnusedSymbol .explicitParams, pos))
429+ end checkExplicit
430+ if ! infos.skip(m) then
431+ checkExplicit()
428432 else if sym.is(Param ) then
433+ def forgiven (sym : Symbol ) =
434+ val dd = defn
435+ sym.name.is(ContextFunctionParamName ) // a ubiquitous parameter
436+ || sym.owner.hasAnnotation(dd.UnusedAnnot ) // param of unused method
437+ || sym.info.typeSymbol.match // more ubiquity
438+ case dd.DummyImplicitClass | dd.SubTypeClass | dd.SameTypeClass => true
439+ case _ => false
440+ || sym.info.isSingleton // DSL friendly
429441 val m = sym.owner
430442 if ctx.settings.WunusedHas .implicits
431443 && ! infos.skip(m)
444+ && ! forgiven(sym)
432445 then
433- warnings.addOne((UnusedSymbolMessage .implicitParams, pos))
446+ warnings.addOne((UnusedSymbol .implicitParams, pos))
434447 else if sym.isLocalToBlock then
435448 if ctx.settings.WunusedHas .locals then
436- warnings.addOne((UnusedSymbolMessage .localDefs, pos))
449+ warnings.addOne((UnusedSymbol .localDefs, pos))
437450 for imp <- infos.imps; sel <- imp.selectors if ! sel.isImportExclusion && ! infos.sels(sel) do
438- warnings.addOne((UnusedSymbolMessage .imports, sel.srcPos))
451+ warnings.addOne((UnusedSymbol .imports, sel.srcPos))
439452 warnings.result().sortInPlaceBy(_._2.span.point).foreach(report.warning(_, _))
440453
441454 extension (nm : Name )
@@ -473,6 +486,13 @@ object CheckUnused:
473486 extension (imp : Import )
474487 /** Generated import of cases from enum companion. */
475488 def isGeneratedByEnum (using Context ): Boolean =
476- imp.symbol.exists && imp.symbol.owner.is(Flags .Enum , butNot = Flags .Case )
489+ imp.symbol.exists && imp.symbol.owner.is(Enum , butNot = Case )
490+
491+ /** Checks if import selects a def that is transparent and inline. */
492+ def isTransparentInline (using Context ): Boolean =
493+ val qual = imp.expr
494+ imp.selectors.exists: sel =>
495+ val importedMembers = qual.tpe.member(sel.name).alternatives
496+ importedMembers.exists(_.symbol.isAllOf(Transparent | Inline ))
477497
478498end CheckUnused
0 commit comments