Skip to content

Commit def4dd7

Browse files
committed
Detect anonymized patvar in given pattern
1 parent c49ff71 commit def4dd7

File tree

3 files changed

+84
-45
lines changed

3 files changed

+84
-45
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+54-43
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import util.{Property, SourceFile, SourcePosition, SrcPos, Chars}
1313
import config.{Feature, Config}
1414
import config.Feature.{sourceVersion, migrateTo3, enabled}
1515
import config.SourceVersion.*
16-
import collection.mutable
1716
import reporting.*
1817
import printing.Formatting.hl
1918
import config.Printers
2019
import parsing.Parsers
20+
import dotty.tools.dotc.util.chaining.*
2121

2222
import scala.annotation.{unchecked as _, *}, internal.sharable
23+
import scala.collection.mutable, mutable.ListBuffer
2324

2425
object desugar {
2526
import untpd.*
@@ -1343,7 +1344,7 @@ object desugar {
13431344
)).withSpan(tree.span)
13441345
end makePolyFunctionType
13451346

1346-
/** Invent a name for an anonympus given of type or template `impl`. */
1347+
/** Invent a name for an anonymous given of type or template `impl`. */
13471348
def inventGivenName(impl: Tree)(using Context): SimpleName =
13481349
val str = impl match
13491350
case impl: Template =>
@@ -2044,33 +2045,35 @@ object desugar {
20442045
makeCaseLambda(CaseDef(gen.pat, EmptyTree, body) :: Nil, matchCheckMode)
20452046
}
20462047

2047-
def hasGivenBind(pat: Tree): Boolean = pat.existsSubTree {
2048-
case pat @ Bind(_, pat1) => pat.mods.is(Given)
2048+
def hasGivenBind(pat: Tree): Boolean = pat.existsSubTree:
2049+
case pat @ Bind(_, _) => pat.mods.is(Given)
20492050
case _ => false
2050-
}
20512051

20522052
/** Does this pattern define any given bindings */
20532053
def isNestedGivenPattern(pat: Tree): Boolean = pat match
2054-
case pat @ Bind(_, pat1) => hasGivenBind(pat1)
2054+
case Bind(_, pat) => hasGivenBind(pat)
20552055
case _ => hasGivenBind(pat)
20562056

20572057
/** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
20582058
* it in a Bind with a fresh name. Return the transformed pattern, and the identifier
20592059
* that refers to the bound variable for the pattern. Wildcard Binds are
20602060
* also replaced by Binds with fresh names.
20612061
*/
2062-
def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
2063-
case bind @ Bind(name, pat1) =>
2064-
if name == nme.WILDCARD then
2065-
val name = UniqueName.fresh()
2066-
(cpy.Bind(pat)(name, pat1).withMods(bind.mods), Ident(name))
2067-
else (pat, Ident(name))
2062+
def makeIdPat(pat: Tree): (Tree, Ident) = pat match
2063+
case bind @ Bind(nme.WILDCARD, pat) =>
2064+
val name = UniqueName.fresh()
2065+
cpy.Bind(pat)(name, pat)
2066+
.withMods(bind.mods)
2067+
.withSpan(bind.span)
2068+
.withAttachment(PatternVar, ())
2069+
->
2070+
Ident(name)
2071+
case Bind(name, _) => (pat, Ident(name))
20682072
case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
20692073
case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
20702074
case _ =>
20712075
val name = UniqueName.fresh()
20722076
(Bind(name, pat), Ident(name))
2073-
}
20742077

20752078
/** Make a pattern filter:
20762079
* rhs.withFilter { case pat => true case _ => false }
@@ -2173,36 +2176,44 @@ object desugar {
21732176
case (gen: GenFrom) :: (rest @ (GenFrom(_, _, _) :: _)) =>
21742177
val cont = makeFor(mapName, flatMapName, rest, body)
21752178
Apply(rhsSelect(gen, flatMapName), makeLambda(gen, cont))
2176-
case (gen: GenFrom) :: rest
2177-
if sourceVersion.enablesBetterFors
2178-
&& rest.dropWhile(_.isInstanceOf[GenAlias]).headOption.forall(e => e.isInstanceOf[GenFrom]) // possible aliases followed by a generator or end of for
2179-
&& !rest.takeWhile(_.isInstanceOf[GenAlias]).exists(a => isNestedGivenPattern(a.asInstanceOf[GenAlias].pat)) =>
2180-
val cont = makeFor(mapName, flatMapName, rest, body)
2181-
val selectName =
2182-
if rest.exists(_.isInstanceOf[GenFrom]) then flatMapName
2183-
else mapName
2184-
val aply = Apply(rhsSelect(gen, selectName), makeLambda(gen, cont))
2185-
markTrailingMap(aply, gen, selectName)
2186-
aply
2187-
case (gen: GenFrom) :: (rest @ GenAlias(_, _) :: _) =>
2188-
val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias])
2189-
val pats = valeqs map { case GenAlias(pat, _) => pat }
2190-
val rhss = valeqs map { case GenAlias(_, rhs) => rhs }
2191-
val (defpat0, id0) = makeIdPat(gen.pat)
2192-
val (defpats, ids) = (pats map makeIdPat).unzip
2193-
val pdefs = valeqs.lazyZip(defpats).lazyZip(rhss).map { (valeq, defpat, rhs) =>
2194-
val mods = defpat match
2195-
case defTree: DefTree => defTree.mods
2196-
case _ => Modifiers()
2197-
makePatDef(valeq, mods, defpat, rhs)
2198-
}
2199-
val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ())))
2200-
val allpats = gen.pat :: pats
2201-
val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore)
2202-
makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
2179+
case (gen: GenFrom) :: (tail @ GenAlias(_, _) :: _) =>
2180+
val (valeqs, suffix) = tail.span(_.isInstanceOf[GenAlias])
2181+
// possible aliases followed by a generator or end of for, when betterFors.
2182+
// exclude value definitions with a given pattern (given T = x)
2183+
val better = sourceVersion.enablesBetterFors
2184+
&& suffix.headOption.forall(_.isInstanceOf[GenFrom])
2185+
&& !valeqs.exists(a => isNestedGivenPattern(a.asInstanceOf[GenAlias].pat))
2186+
if better then
2187+
val cont = makeFor(mapName, flatMapName, enums = tail, body)
2188+
val selectName =
2189+
if suffix.exists(_.isInstanceOf[GenFrom]) then flatMapName
2190+
else mapName
2191+
Apply(rhsSelect(gen, selectName), makeLambda(gen, cont))
2192+
.tap(markTrailingMap(_, gen, selectName))
2193+
else
2194+
val pats = valeqs.map { case GenAlias(pat, _) => pat }
2195+
val rhss = valeqs.map { case GenAlias(_, rhs) => rhs }
2196+
val (defpat0, id0) = makeIdPat(gen.pat)
2197+
val (defpats, ids) = pats.map(makeIdPat).unzip
2198+
val pdefs = valeqs.lazyZip(defpats).lazyZip(rhss).map { (valeq, defpat, rhs) =>
2199+
val mods = defpat match
2200+
case defTree: DefTree => defTree.mods
2201+
case _ => Modifiers()
2202+
makePatDef(valeq, mods, defpat, rhs)
2203+
}
2204+
val rhs1 =
2205+
val enums = GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil
2206+
val body = Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ()))
2207+
makeFor(nme.map, nme.flatMap, enums, body)
2208+
val allpats = gen.pat :: pats
2209+
val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore)
2210+
makeFor(mapName, flatMapName, enums = vfrom1 :: suffix, body)
2211+
end if
22032212
case (gen: GenFrom) :: test :: rest =>
2204-
val filtered = Apply(rhsSelect(gen, nme.withFilter), makeLambda(gen, test))
2205-
val genFrom = GenFrom(gen.pat, filtered, if sourceVersion.enablesBetterFors then GenCheckMode.Filtered else GenCheckMode.Ignore)
2213+
val genFrom =
2214+
val filtered = Apply(rhsSelect(gen, nme.withFilter), makeLambda(gen, test))
2215+
val mode = if sourceVersion.enablesBetterFors then GenCheckMode.Filtered else GenCheckMode.Ignore
2216+
GenFrom(gen.pat, filtered, mode)
22062217
makeFor(mapName, flatMapName, genFrom :: rest, body)
22072218
case GenAlias(_, _) :: _ if sourceVersion.enablesBetterFors =>
22082219
val (valeqs, rest) = enums.span(_.isInstanceOf[GenAlias])
@@ -2396,7 +2407,7 @@ object desugar {
23962407
* without duplicates
23972408
*/
23982409
private def getVariables(tree: Tree, shouldAddGiven: Context ?=> Bind => Boolean)(using Context): List[VarInfo] = {
2399-
val buf = mutable.ListBuffer[VarInfo]()
2410+
val buf = ListBuffer.empty[VarInfo]
24002411
def seenName(name: Name) = buf exists (_._1.name == name)
24012412
def add(named: NameTree, t: Tree): Unit =
24022413
if (!seenName(named.name) && named.name.isTermName) buf += ((named, t))

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -444,10 +444,15 @@ object CheckUnused:
444444
then
445445
imps.put(imp, ())
446446
case tree: Bind =>
447-
if !tree.name.isInstanceOf[DerivedName] && !tree.name.is(WildcardParamName) then
447+
def addBindAt(pos: SrcPos): Unit =
448448
if tree.hasAttachment(NoWarn) then
449449
nowarn.addOne(tree.symbol)
450-
pats.addOne((tree.symbol, tree.namePos))
450+
pats.addOne((tree.symbol, pos))
451+
if !tree.name.isInstanceOf[DerivedName] && !tree.name.is(WildcardParamName) then
452+
addBindAt(tree.namePos)
453+
else if tree.hasAttachment(PatternVar) then
454+
addBindAt(tree.srcPos.sourcePos)
455+
addRef(tree.symbol)
451456
case tree: NamedDefTree =>
452457
if tree.hasAttachment(PatternVar) then
453458
if !tree.name.isInstanceOf[DerivedName] then
@@ -611,6 +616,7 @@ object CheckUnused:
611616
if pos.span.isSynthetic then pos else pos.sourcePos.withSpan(pos.span.toSynthetic)
612617
// patvars in for comprehensions share the pos of where the name was introduced
613618
val byPos = infos.pats.groupMap(uniformPos(_, _))((sym, pos) => sym)
619+
//println(infos.pats.mkString("PATVARS\n", "\n", "\n----\n"))
614620
for (pos, syms) <- byPos if pos.span.exists && !syms.exists(_.hasAnnotation(defn.UnusedAnnot)) do
615621
if !syms.exists(infos.refs(_)) then
616622
if !syms.exists(v => !v.isLocal && !v.is(Private) || infos.nowarn(v)) then

tests/pos/i23119.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//> using options -Wunused:patvars -Werror
2+
def make: IndexedSeq[FalsePositive] =
3+
for {
4+
i <- 1 to 2
5+
given Int = i
6+
fp = FalsePositive()
7+
} yield fp
8+
9+
/* compare to
10+
def alt: IndexedSeq[FalsePositive] =
11+
given String = "hi"
12+
for {
13+
i <- 1 to 2
14+
k @ (j :: Nil) = List(i) // pattern in one var
15+
//j: Int = i // simple assign because irrefutable
16+
fp = FalsePositive(using j)
17+
} yield fp
18+
*/
19+
20+
class FalsePositive(using Int):
21+
def usage(): Unit =
22+
println(summon[Int])

0 commit comments

Comments
 (0)