@@ -498,22 +498,33 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
498
498
499
499
// ValDef was a PatVarDef `val P(x) = ???`
500
500
private def wasPatVarDef (tree : ValDef ): Boolean = tree.hasAttachment[PatVarDefAttachment .type ]
501
+ private def wasPatVarDef (sym : Symbol ): Boolean = sym.hasAttachment[PatVarDefAttachment .type ]
501
502
}
502
503
503
504
class UnusedPrivates extends Traverser {
504
505
import UnusedPrivates .{ignoreNames , nowarn , wasPatVarDef }
505
- def isEffectivelyPrivate (sym : Symbol ): Boolean = false
506
+ def isEffectivelyPrivate (sym : Symbol ): Boolean = false // see REPL
506
507
val defnTrees = ListBuffer .empty[MemberDef ]
507
508
val targets = mutable.Set .empty[Symbol ]
508
509
val setVars = mutable.Set .empty[Symbol ]
509
510
val treeTypes = mutable.Set .empty[Type ]
510
511
val params = mutable.Set .empty[Symbol ]
511
512
val patvars = ListBuffer .empty[Tree /* Bind|ValDef*/ ]
513
+ val ignore = mutable.Set .empty[Symbol ] // nowarn
512
514
513
515
val annots = mutable.Set .empty[AnnotationInfo ] // avoid revisiting annotations of symbols and types
514
516
515
517
def recordReference (sym : Symbol ): Unit = targets.addOne(sym)
516
518
519
+ def checkNowarn (tree : Tree ): Unit =
520
+ tree match {
521
+ case tree : Bind =>
522
+ if (nowarn(tree)) ignore += tree.symbol
523
+ case tree : ValDef =>
524
+ if (nowarn(tree)) ignore += tree.symbol
525
+ case _ =>
526
+ }
527
+
517
528
def qualifiesTerm (sym : Symbol ) = (
518
529
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock || isEffectivelyPrivate(sym))
519
530
&& ! nme.isLocalName(sym.name)
@@ -528,41 +539,60 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
528
539
&& (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym))
529
540
)
530
541
def isExisting (sym : Symbol ) = sym != null && sym.exists
531
- def addPatVar (t : Tree ) = patvars += t
542
+ def addPatVar (t : Tree ) = {
543
+ checkNowarn(t)
544
+ patvars += t
545
+ }
532
546
533
547
// so trivial that it never consumes params
534
548
def isTrivial (rhs : Tree ): Boolean =
535
549
rhs.symbol == Predef_??? || rhs.tpe == null || rhs.tpe =:= NothingTpe || (rhs match {
536
550
case Literal (_) => true
537
- case _ => isConstantType(rhs.tpe) || isSingleType(rhs.tpe)
551
+ case _ => isConstantType(rhs.tpe) || isSingleType(rhs.tpe) || rhs. isInstanceOf [ This ]
538
552
})
539
553
540
554
override def traverse (t : Tree ): Unit = {
541
- val sym = t.symbol
542
555
t match {
543
- case treeInfo.Applied (fun, _, _) if t.hasAttachment[ForAttachment .type ] && fun.symbol != null && isTupleSymbol(fun.symbol.owner.companion) =>
544
- return // ignore tupling of assignments
545
- case m : MemberDef if qualifies(sym) && ! t.isErrorTyped =>
556
+ case t : ValDef if wasPatVarDef(t) => // include field excluded by qualifies test
557
+ if (settings.warnUnusedPatVars)
558
+ addPatVar(t)
559
+ case t : MemberDef if qualifies(t.symbol) && ! t.isErrorTyped =>
560
+ val sym = t.symbol
546
561
t match {
547
- case t : ValDef =>
548
- if (wasPatVarDef(t)) {
549
- if (settings.warnUnusedPatVars && ! nowarn(t)) addPatVar(t)
550
- }
551
- else defnTrees += m
552
562
case DefDef (_, _, _, vparamss, _, rhs) if ! sym.isAbstract && ! sym.isDeprecated && ! sym.isMacro =>
553
- if (isSuppressed(sym)) return
563
+ if (isSuppressed(sym)) return // ignore params and rhs of @unused def
554
564
if (sym.isPrimaryConstructor)
555
565
for (cpa <- sym.owner.constrParamAccessors if cpa.isPrivateLocal) params += cpa
556
566
else if (sym.isSynthetic && sym.isImplicit) return
557
567
else if (! sym.isConstructor && ! sym.isVar && ! isTrivial(rhs))
558
568
for (vs <- vparamss; v <- vs) if (! isSingleType(v.symbol.tpe)) params += v.symbol
559
- defnTrees += m
569
+ if (sym.isGetter && wasPatVarDef(sym.accessed)) {
570
+ if (settings.warnUnusedPatVars)
571
+ addPatVar(t)
572
+ }
573
+ else defnTrees += t
560
574
case TypeDef (_, _, _, _) =>
561
575
if (! sym.isAbstract && ! sym.isDeprecated)
562
- defnTrees += m
576
+ defnTrees += t
563
577
case _ =>
564
- defnTrees += m
578
+ defnTrees += t
565
579
}
580
+ case Match (selector, cases) =>
581
+ // don't warn when a patvar redefines the selector ident: x match { case x: X => }
582
+ def allowVariableBindings (n : Name , pat : Tree ): Unit =
583
+ pat.foreach {
584
+ case bind @ Bind (`n`, _) => bind.updateAttachment(NoWarnAttachment )
585
+ case _ =>
586
+ }
587
+ def allow (n : Name ): Unit = cases.foreach(k => allowVariableBindings(n, k.pat))
588
+ def loop (selector : Tree ): Unit =
589
+ selector match {
590
+ case Ident (n) => allow(n)
591
+ case Typed (expr, _) => loop(expr)
592
+ case Select (This (_), n) => allow(n)
593
+ case _ =>
594
+ }
595
+ loop(selector)
566
596
case CaseDef (pat, _, _) if settings.warnUnusedPatVars && ! t.isErrorTyped =>
567
597
def absolveVariableBindings (app : Apply , args : List [Tree ]): Unit =
568
598
treeInfo.dissectApplied(app).core.tpe match {
@@ -580,18 +610,30 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
580
610
case _ =>
581
611
}
582
612
pat.foreach {
583
- case b @ Bind (n, _) if ! nowarn(b) && n != nme.DEFAULT_CASE => addPatVar(b)
613
+ case b @ Bind (n, _) if n != nme.DEFAULT_CASE => addPatVar(b)
584
614
case _ =>
585
615
}
586
- case _ : RefTree => if (isExisting(sym) && ! currentOwner.hasTransOwner(sym)) recordReference(sym)
616
+ case t : RefTree =>
617
+ val sym = t.symbol
618
+ if (isExisting(sym) && ! currentOwner.hasTransOwner(sym) && ! t.hasAttachment[ForAttachment .type ])
619
+ recordReference(sym)
587
620
case Assign (lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol
588
621
case Function (ps, _) if ! t.isErrorTyped =>
589
- for (p <- ps)
622
+ for (p <- ps) {
590
623
if (wasPatVarDef(p)) {
591
- if (settings.warnUnusedPatVars && ! nowarn(p) )
624
+ if (settings.warnUnusedPatVars)
592
625
addPatVar(p)
593
626
}
594
- else if (settings.warnUnusedParams && ! nowarn(p) && ! p.symbol.isSynthetic) params += p.symbol
627
+ else {
628
+ if (settings.warnUnusedParams && ! p.symbol.isSynthetic) {
629
+ checkNowarn(p)
630
+ params += p.symbol
631
+ }
632
+ }
633
+ }
634
+ case treeInfo.Applied (fun, _, _)
635
+ if t.hasAttachment[ForAttachment .type ] && fun.symbol != null && isTupleSymbol(fun.symbol.owner.companion) =>
636
+ return // ignore tupling of assignments
595
637
case Literal (_) =>
596
638
t.attachments.get[OriginalTreeAttachment ].foreach(ota => traverse(ota.original))
597
639
case tt : TypeTree =>
@@ -638,8 +680,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
638
680
}
639
681
}
640
682
641
- if (sym != null && sym .exists)
642
- for (annot <- sym .annotations)
683
+ if (t.symbol != null && t.symbol .exists)
684
+ for (annot <- t.symbol .annotations)
643
685
descend(annot)
644
686
645
687
super .traverse(t)
@@ -679,6 +721,9 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
679
721
targets.exists(s => s.isParameter
680
722
&& s.name == m.name && s.owner.isConstructor && s.owner.owner == m.owner) // exclude ctor params
681
723
))
724
+ && ! (m.info.typeSymbol == UnitClass )
725
+ && ! (m.owner.isClass && m.owner.thisType.baseClasses.contains(AnnotationClass ))
726
+ && ! ignore(m)
682
727
)
683
728
def unusedTypes = defnTrees.iterator.filter(t => isUnusedType(t.symbol))
684
729
def unusedTerms = {
@@ -700,11 +745,36 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
700
745
def unusedParams = params.iterator.filter(isUnusedParam)
701
746
def inDefinedAt (p : Symbol ) = p.owner.isMethod && p.owner.name == nme.isDefinedAt && p.owner.owner.isAnonymousFunction
702
747
def unusedPatVars = {
703
- // in elaboration of for comprehensions, patterns are duplicated; track a patvar by its start position; "original" has a range pos
704
- val all = patvars.filterInPlace(_.pos.isDefined)
705
- val byPos = all.groupBy(_.pos.start)
706
- def isUnusedPatVar (t : Tree ): Boolean = byPos(t.pos.start).forall(p => ! targets(p.symbol))
707
- all.iterator.filter(p => p.pos.isOpaqueRange && isUnusedTerm(p.symbol) && isUnusedPatVar(p) && ! inDefinedAt(p.symbol))
748
+ // in elaboration of for comprehensions, patterns are duplicated;
749
+ // track a patvar by its symbol position; "original" has a range pos
750
+ val all = patvars.filterInPlace(_.symbol.pos.isDefined)
751
+ val byPos = all.groupBy(_.symbol.pos.start)
752
+ def isNotPrivateOrLocal (s : Symbol ) = s.hasAccessorFlag && s.hasNoFlags(PRIVATE | LOCAL )
753
+ def isUnusedPatVar (t : Tree ): Boolean =
754
+ byPos(t.symbol.pos.start).forall(p =>
755
+ ! targets(p.symbol)
756
+ && ! isNotPrivateOrLocal(p.symbol)
757
+ && ! ignore(p.symbol)
758
+ )
759
+ // the "original" tree has an opaque range;
760
+ // for multi-var patdef, tree pos is transparent but sym pos is opaque;
761
+ // use the field as the primary definition, and also remove it from targets
762
+ // if it has a getter (in which case it has the "local" name to disambiguate).
763
+ // Note that for uni-var patdef `val Some(x)`, tree pos is opaque.
764
+ def isPrimaryPatVarDefinition (p : Tree ): Boolean =
765
+ p.symbol.pos.isOpaqueRange && {
766
+ val primary = p.pos.isOpaqueRange || p.symbol.isPrivateLocal
767
+ if (primary && nme.isLocalName(p.symbol.name))
768
+ targets.subtractOne(p.symbol) // field is trivially accessed by its getter if it has one
769
+ primary
770
+ }
771
+ all.iterator.filter(p =>
772
+ isPrimaryPatVarDefinition(p)
773
+ && isUnusedTerm(p.symbol)
774
+ && isUnusedPatVar(p)
775
+ && ! nme.isFreshTermName(p.symbol.name)
776
+ && ! inDefinedAt(p.symbol)
777
+ )
708
778
}
709
779
}
710
780
@@ -822,7 +892,7 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
822
892
}
823
893
if (settings.warnUnusedPatVars)
824
894
for (v <- unusedPrivates.unusedPatVars)
825
- emitUnusedWarning(v.pos, s " pattern var ${v.symbol.name} in ${v.symbol.owner} is never used " , WarningCategory .UnusedPatVars , v.symbol)
895
+ emitUnusedWarning(v.symbol. pos, s " pattern var ${v.symbol.name.dropLocal } in ${v.symbol.owner} is never used " , WarningCategory .UnusedPatVars , v.symbol)
826
896
if (settings.warnUnusedParams) {
827
897
// don't warn unused args of overriding methods (or methods matching in self-type)
828
898
def isImplementation (m : Symbol ): Boolean = m.isMethod && {
0 commit comments