Skip to content

Commit 4379f96

Browse files
committed
Restore functionality
1 parent 0038e4c commit 4379f96

File tree

4 files changed

+122
-95
lines changed

4 files changed

+122
-95
lines changed

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

Lines changed: 106 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,18 @@ import dotty.tools.dotc.ast.tpd.*
77
import dotty.tools.dotc.ast.untpd, untpd.ImportSelector
88
import dotty.tools.dotc.config.ScalaSettings
99
import dotty.tools.dotc.core.Contexts.*
10-
import dotty.tools.dotc.core.Decorators.{em, i, toMessage}
11-
import dotty.tools.dotc.core.Denotations.SingleDenotation
1210
import dotty.tools.dotc.core.Flags.*
13-
import dotty.tools.dotc.core.Phases.Phase
1411
import 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}
1914
import dotty.tools.dotc.core.NameOps.isReplWrapperName
2015
import dotty.tools.dotc.core.NameKinds.{ContextFunctionParamName, WildcardParamName}
2116
import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol, defn, isDeprecated}
2217
import dotty.tools.dotc.report
23-
import dotty.tools.dotc.reporting.{Message, UnusedSymbol as UnusedSymbolMessage}
18+
import dotty.tools.dotc.reporting.UnusedSymbol
2419
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
2520
import dotty.tools.dotc.typer.ImportInfo
2621
import dotty.tools.dotc.util.{Property, SrcPos}
27-
import dotty.tools.dotc.util.Spans.Span
2822
import dotty.tools.dotc.util.chaining.*
2923

3024
import 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

478498
end CheckUnused

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
215215
++ sym.annotations)
216216
else
217217
if sym.is(Param) then
218-
sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
218+
// @unused is getter/setter but we want it on ordinary method params
219+
if !sym.owner.is(Method) || sym.owner.isConstructor then
220+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
219221
else if sym.is(ParamAccessor) then
220222
// @publicInBinary is not a meta-annotation and therefore not kept by `keepAnnotationsCarrying`
221223
val publicInBinaryAnnotOpt = sym.getAnnotation(defn.PublicInBinaryAnnot)

tests/warn/i15503c.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//> using options -Wunused:privates -source:3.3
1+
//> using options -Wunused:privates -source:3.3
22

33
trait C
44
class A:
@@ -33,17 +33,22 @@ class A:
3333
var w = 2 // OK
3434

3535
package foo.test.constructors:
36-
case class A private (x:Int) // OK
36+
case class A private (x: Int) // OK
3737
class B private (val x: Int) // OK
38-
class C private (private val x: Int) // warn
38+
object B { def default = B(42) }
39+
class C private (private val xy: Int) // warn
40+
object C { def default = C(42) }
3941
class D private (private val x: Int): // OK
4042
def y = x
43+
object D { def default = D(42) }
4144
class E private (private var x: Int): // warn not set
4245
def y = x
46+
object E { def default = E(42) }
4347
class F private (private var x: Int): // OK
4448
def y =
4549
x = 3
4650
x
51+
object F { def default = F(42) }
4752

4853
package test.foo.i16682:
4954
object myPackage:
@@ -55,4 +60,4 @@ package test.foo.i16682:
5560
case _ => println("NaN")
5661
}
5762

58-
def f = myPackage.isInt("42")
63+
def f = myPackage.isInt("42")

tests/warn/i15503i.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ package foo.test.possibleclasses.withvar:
141141

142142
case class AllCaseUsed(
143143
k: Int, // OK
144-
private var y: Int // OK
144+
private var y: Int // warn unset
145145
)(
146146
s: Int, // OK
147147
var tt: Int, // OK global scope can be set somewhere else
@@ -202,13 +202,13 @@ package foo.test.i16926:
202202
def hello(): Unit =
203203
for {
204204
i <- (0 to 10).toList
205-
(a, b) = "hello" -> "world" // OK
205+
(a, b) = "hello" -> "world" // warn // warn TODO patvars
206206
} yield println(s"$a $b")
207207

208208
package foo.test.i16925:
209209
def hello =
210210
for {
211-
i <- 1 to 2 if true
211+
i <- 1 to 2 if true // warn TODO patvar
212212
_ = println(i) // OK
213213
} yield ()
214214

@@ -296,7 +296,7 @@ package foo.test.i17175:
296296
val continue = true
297297
def foo =
298298
for {
299-
i <- 1.until(10) // OK
299+
i <- 1.until(10) // warn patvar TODO
300300
if continue
301301
} {
302302
println(i)

0 commit comments

Comments
 (0)