Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7c68e40

Browse files
authored
Merge pull request scala#11012 from som-snytt/issue/13095-patvar
Collect nowarn symbols instead of skipping them
2 parents 0cc14d0 + 95f204e commit 7c68e40

File tree

20 files changed

+229
-92
lines changed

20 files changed

+229
-92
lines changed

build.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories
197197
run / fork := true,
198198
run / connectInput := true,
199199
Compile / scalacOptions ++= Seq("-feature", "-Xlint",
200+
//"-Wunused:patvars",
201+
//"-Wunused:params",
200202
//"-Vprint",
201203
//"-Xmaxerrs", "5", "-Xmaxwarns", "5", // uncomment for ease of development while breaking things
202204
// work around https://github.com/scala/bug/issues/11534
@@ -451,6 +453,7 @@ lazy val library = configureAsSubproject(project)
451453
name := "scala-library",
452454
description := "Scala Standard Library",
453455
Compile / scalacOptions ++= Seq("-sourcepath", (Compile / scalaSource).value.toString),
456+
Compile / scalacOptions ++= Seq("-Wconf:msg=method box|method anyValClass:s"), // unused params in patched src
454457
Compile / doc / scalacOptions ++= {
455458
val libraryAuxDir = (ThisBuild / baseDirectory).value / "src/library-aux"
456459
Seq(

src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -498,22 +498,33 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
498498

499499
// ValDef was a PatVarDef `val P(x) = ???`
500500
private def wasPatVarDef(tree: ValDef): Boolean = tree.hasAttachment[PatVarDefAttachment.type]
501+
private def wasPatVarDef(sym: Symbol): Boolean = sym.hasAttachment[PatVarDefAttachment.type]
501502
}
502503

503504
class UnusedPrivates extends Traverser {
504505
import UnusedPrivates.{ignoreNames, nowarn, wasPatVarDef}
505-
def isEffectivelyPrivate(sym: Symbol): Boolean = false
506+
def isEffectivelyPrivate(sym: Symbol): Boolean = false // see REPL
506507
val defnTrees = ListBuffer.empty[MemberDef]
507508
val targets = mutable.Set.empty[Symbol]
508509
val setVars = mutable.Set.empty[Symbol]
509510
val treeTypes = mutable.Set.empty[Type]
510511
val params = mutable.Set.empty[Symbol]
511512
val patvars = ListBuffer.empty[Tree /*Bind|ValDef*/]
513+
val ignore = mutable.Set.empty[Symbol] // nowarn
512514

513515
val annots = mutable.Set.empty[AnnotationInfo] // avoid revisiting annotations of symbols and types
514516

515517
def recordReference(sym: Symbol): Unit = targets.addOne(sym)
516518

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+
517528
def qualifiesTerm(sym: Symbol) = (
518529
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock || isEffectivelyPrivate(sym))
519530
&& !nme.isLocalName(sym.name)
@@ -528,41 +539,60 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
528539
&& (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym))
529540
)
530541
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+
}
532546

533547
// so trivial that it never consumes params
534548
def isTrivial(rhs: Tree): Boolean =
535549
rhs.symbol == Predef_??? || rhs.tpe == null || rhs.tpe =:= NothingTpe || (rhs match {
536550
case Literal(_) => true
537-
case _ => isConstantType(rhs.tpe) || isSingleType(rhs.tpe)
551+
case _ => isConstantType(rhs.tpe) || isSingleType(rhs.tpe) || rhs.isInstanceOf[This]
538552
})
539553

540554
override def traverse(t: Tree): Unit = {
541-
val sym = t.symbol
542555
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
546561
t match {
547-
case t: ValDef =>
548-
if (wasPatVarDef(t)) {
549-
if (settings.warnUnusedPatVars && !nowarn(t)) addPatVar(t)
550-
}
551-
else defnTrees += m
552562
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
554564
if (sym.isPrimaryConstructor)
555565
for (cpa <- sym.owner.constrParamAccessors if cpa.isPrivateLocal) params += cpa
556566
else if (sym.isSynthetic && sym.isImplicit) return
557567
else if (!sym.isConstructor && !sym.isVar && !isTrivial(rhs))
558568
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
560574
case TypeDef(_, _, _, _) =>
561575
if (!sym.isAbstract && !sym.isDeprecated)
562-
defnTrees += m
576+
defnTrees += t
563577
case _ =>
564-
defnTrees += m
578+
defnTrees += t
565579
}
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)
566596
case CaseDef(pat, _, _) if settings.warnUnusedPatVars && !t.isErrorTyped =>
567597
def absolveVariableBindings(app: Apply, args: List[Tree]): Unit =
568598
treeInfo.dissectApplied(app).core.tpe match {
@@ -580,18 +610,30 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
580610
case _ =>
581611
}
582612
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)
584614
case _ =>
585615
}
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)
587620
case Assign(lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol
588621
case Function(ps, _) if !t.isErrorTyped =>
589-
for (p <- ps)
622+
for (p <- ps) {
590623
if (wasPatVarDef(p)) {
591-
if (settings.warnUnusedPatVars && !nowarn(p))
624+
if (settings.warnUnusedPatVars)
592625
addPatVar(p)
593626
}
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
595637
case Literal(_) =>
596638
t.attachments.get[OriginalTreeAttachment].foreach(ota => traverse(ota.original))
597639
case tt: TypeTree =>
@@ -638,8 +680,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
638680
}
639681
}
640682

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)
643685
descend(annot)
644686

645687
super.traverse(t)
@@ -679,6 +721,9 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
679721
targets.exists(s => s.isParameter
680722
&& s.name == m.name && s.owner.isConstructor && s.owner.owner == m.owner) // exclude ctor params
681723
))
724+
&& !(m.info.typeSymbol == UnitClass)
725+
&& !(m.owner.isClass && m.owner.thisType.baseClasses.contains(AnnotationClass))
726+
&& !ignore(m)
682727
)
683728
def unusedTypes = defnTrees.iterator.filter(t => isUnusedType(t.symbol))
684729
def unusedTerms = {
@@ -700,11 +745,36 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
700745
def unusedParams = params.iterator.filter(isUnusedParam)
701746
def inDefinedAt(p: Symbol) = p.owner.isMethod && p.owner.name == nme.isDefinedAt && p.owner.owner.isAnonymousFunction
702747
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+
)
708778
}
709779
}
710780

@@ -822,7 +892,7 @@ trait TypeDiagnostics extends splain.SplainDiagnostics {
822892
}
823893
if (settings.warnUnusedPatVars)
824894
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)
826896
if (settings.warnUnusedParams) {
827897
// don't warn unused args of overriding methods (or methods matching in self-type)
828898
def isImplementation(m: Symbol): Boolean = m.isMethod && {

src/library/scala/Array.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,16 @@ object Array {
124124
* @see `java.util.Arrays#copyOf`
125125
*/
126126
def copyOf[A](original: Array[A], newLength: Int): Array[A] = ((original: @unchecked) match {
127-
case x: Array[BoxedUnit] => newUnitArray(newLength).asInstanceOf[Array[A]]
128-
case x: Array[AnyRef] => java.util.Arrays.copyOf(x, newLength)
129-
case x: Array[Int] => java.util.Arrays.copyOf(x, newLength)
130-
case x: Array[Double] => java.util.Arrays.copyOf(x, newLength)
131-
case x: Array[Long] => java.util.Arrays.copyOf(x, newLength)
132-
case x: Array[Float] => java.util.Arrays.copyOf(x, newLength)
133-
case x: Array[Char] => java.util.Arrays.copyOf(x, newLength)
134-
case x: Array[Byte] => java.util.Arrays.copyOf(x, newLength)
135-
case x: Array[Short] => java.util.Arrays.copyOf(x, newLength)
136-
case x: Array[Boolean] => java.util.Arrays.copyOf(x, newLength)
127+
case original: Array[BoxedUnit] => newUnitArray(newLength).asInstanceOf[Array[A]]
128+
case original: Array[AnyRef] => java.util.Arrays.copyOf(original, newLength)
129+
case original: Array[Int] => java.util.Arrays.copyOf(original, newLength)
130+
case original: Array[Double] => java.util.Arrays.copyOf(original, newLength)
131+
case original: Array[Long] => java.util.Arrays.copyOf(original, newLength)
132+
case original: Array[Float] => java.util.Arrays.copyOf(original, newLength)
133+
case original: Array[Char] => java.util.Arrays.copyOf(original, newLength)
134+
case original: Array[Byte] => java.util.Arrays.copyOf(original, newLength)
135+
case original: Array[Short] => java.util.Arrays.copyOf(original, newLength)
136+
case original: Array[Boolean] => java.util.Arrays.copyOf(original, newLength)
137137
}).asInstanceOf[Array[A]]
138138

139139
/** Copy one array to another, truncating or padding with default values (if

src/library/scala/collection/Iterable.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,10 +725,12 @@ trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] with Iterable
725725
* @return a new $coll which contains all elements
726726
* of this $coll followed by all elements of `suffix`.
727727
*/
728-
def concat[B >: A](suffix: IterableOnce[B]): CC[B] = iterableFactory.from(suffix match {
729-
case xs: Iterable[B] => new View.Concat(this, xs)
730-
case xs => iterator ++ suffix.iterator
731-
})
728+
def concat[B >: A](suffix: IterableOnce[B]): CC[B] = iterableFactory.from {
729+
suffix match {
730+
case suffix: Iterable[B] => new View.Concat(this, suffix)
731+
case suffix => iterator ++ suffix.iterator
732+
}
733+
}
732734

733735
/** Alias for `concat` */
734736
@inline final def ++ [B >: A](suffix: IterableOnce[B]): CC[B] = concat(suffix)

src/reflect/scala/reflect/internal/Printers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ trait Printers extends api.Printers { self: SymbolTable =>
850850
if (name.startsWith(nme.WHILE_PREFIX)) {
851851
val If(cond, thenp, _) = rhs: @unchecked
852852
print("while (", cond, ") ")
853-
val Block(list, wh) = thenp: @unchecked
853+
val Block(list, _) = thenp: @unchecked
854854
printColumn(list, "", ";", "")
855855
} else if (name.startsWith(nme.DO_WHILE_PREFIX)) {
856856
val Block(bodyList, If(cond, _, _)) = rhs: @unchecked

src/reflect/scala/reflect/internal/TreeGen.scala

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -693,10 +693,15 @@ abstract class TreeGen {
693693
Apply(Select(qual, meth).setPos(qual.pos).updateAttachment(ForAttachment),
694694
List(makeClosure(pos, pat, body))).setPos(pos)
695695

696-
/* If `pat` is not yet a `Bind` wrap it in one with a fresh name */
696+
/* If `pat` is not yet a `Bind` wrap it in one with a fresh name.
697+
* If the fresh patvar is for tupling in the desugared expression,
698+
* it receives the transparent position of the pattern, so it is never warned about.
699+
* Otherwise, add NoWarnAttachment.
700+
*/
697701
def makeBind(pat: Tree): Bind = pat match {
698702
case pat: Bind => pat
699-
case _ => Bind(freshTermName(), pat).setPos(pat.pos).updateAttachment(NoWarnAttachment)
703+
case _ => Bind(freshTermName(), pat).setPos(pat.pos)
704+
.tap(bind => if (!bind.pos.isTransparent) bind.updateAttachment(NoWarnAttachment))
700705
}
701706

702707
/* A reference to the name bound in Bind `pat`. */
@@ -864,19 +869,11 @@ abstract class TreeGen {
864869
else ValFrom(pat1, mkCheckIfRefutable(pat1, rhs)).setPos(pos)
865870
}
866871

867-
private def unwarnable(pat: Tree): pat.type = {
868-
pat foreach {
869-
case b @ Bind(_, _) => b.updateAttachment(NoWarnAttachment)
870-
case _ =>
871-
}
872-
pat
873-
}
874-
875872
def mkCheckIfRefutable(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator) =
876873
if (treeInfo.isVarPatternDeep(pat)) rhs
877874
else {
878875
val cases = List(
879-
CaseDef(unwarnable(pat.duplicate), EmptyTree, Literal(Constant(true))),
876+
CaseDef(pat.duplicate, EmptyTree, Literal(Constant(true))),
880877
CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
881878
)
882879
val visitor = mkVisitor(cases, checkExhaustive = false, nme.CHECK_IF_REFUTABLE_STRING)
@@ -919,7 +916,7 @@ abstract class TreeGen {
919916
else {
920917
val start = tree.pos.start
921918
val end = start + name.decode.length
922-
rangePos(tree.pos.source, start, start, end) // Bind should get NamePos in parser
919+
rangePos(tree.pos.source, start = start, point = start, end = end) // Bind should get NamePos in parser
923920
}
924921

925922
override def traverse(tree: Tree): Unit = {

src/reflect/scala/reflect/internal/transform/UnCurry.scala

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,10 @@ trait UnCurry {
9999
// while processing one of its superclasses (such as java.lang.Object). Since we
100100
// don't need the more precise `matches` semantics, we only check the symbol, which
101101
// is anyway faster and safer
102-
for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) {
103-
if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) {
104-
varargOverloads += varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info))
105-
}
106-
}
102+
for (decl <- decls)
103+
if (decl.annotations.exists(_.symbol == VarargsClass)
104+
&& mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe)))
105+
varargOverloads += varargForwarderSym(clazz, decl)
107106
if ((parents1 eq parents) && varargOverloads.isEmpty) tp
108107
else {
109108
val newDecls = decls.cloneScope
@@ -120,11 +119,9 @@ trait UnCurry {
120119
}
121120
}
122121

123-
private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = {
122+
private def varargForwarderSym(currentClass: Symbol, origSym: Symbol): Symbol = {
124123
val forwSym = origSym.cloneSymbol(currentClass, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED, origSym.name.toTermName).withoutAnnotations
125124

126-
// we are using `origSym.info`, which contains the type *before* the transformation
127-
// so we still see repeated parameter types (uncurry replaces them with Seq)
128125
def toArrayType(tp: Type, newParam: Symbol): Type = {
129126
val arg = elementType(SeqClass, tp)
130127
val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
@@ -146,6 +143,8 @@ trait UnCurry {
146143
arrayType(elem)
147144
}
148145

146+
// we are using `origSym.info`, which contains the type *before* the transformation
147+
// so we still see repeated parameter types (uncurry replaces them with Seq)
149148
foreach2(forwSym.paramss, origSym.info.paramss){ (fsps, origPs) =>
150149
foreach2(fsps, origPs){ (p, sym) =>
151150
if (definitions.isRepeatedParamType(sym.tpe))

src/reflect/scala/reflect/internal/util/WeakHashSet.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,8 @@ object WeakHashSet {
424424
val defaultInitialCapacity = 16
425425
val defaultLoadFactor = .75
426426

427-
def apply[A <: AnyRef](initialCapacity: Int = WeakHashSet.defaultInitialCapacity, loadFactor: Double = WeakHashSet.defaultLoadFactor) = new WeakHashSet[A](initialCapacity, defaultLoadFactor)
427+
def apply[A <: AnyRef](initialCapacity: Int = defaultInitialCapacity, loadFactor: Double = defaultLoadFactor) =
428+
new WeakHashSet[A](initialCapacity, loadFactor)
428429

429430
def empty[A <: AnyRef]: WeakHashSet[A] = new WeakHashSet[A]()
430431
}

0 commit comments

Comments
 (0)