Skip to content

Commit d38c344

Browse files
committed
Improve checking LHS of Assign
1 parent 88a741b commit d38c344

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

+20-8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
5252
tree
5353

5454
override def transformIdent(tree: Ident)(using Context): tree.type =
55+
refInfos.isAssignment = tree.hasAttachment(AssignmentTarget)
5556
if tree.symbol.exists then
5657
// if in an inline expansion, resolve at summonInline (synthetic pos) or in an enclosing call site
5758
val resolving =
@@ -68,10 +69,12 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
6869
resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
6970
else if tree.hasType then
7071
resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
72+
refInfos.isAssignment = false
7173
tree
7274

7375
// import x.y; y may be rewritten x.y, also import x.z as y
7476
override def transformSelect(tree: Select)(using Context): tree.type =
77+
refInfos.isAssignment = tree.hasAttachment(AssignmentTarget)
7578
val name = tree.removeAttachment(OriginalName).getOrElse(nme.NO_NAME)
7679
inline def isImportable = tree.qualifier.srcPos.isSynthetic
7780
&& tree.qualifier.tpe.match
@@ -92,6 +95,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
9295
resolveUsage(tree.symbol, name, tree.qualifier.tpe)
9396
else if !ignoreTree(tree) then
9497
refUsage(tree.symbol)
98+
refInfos.isAssignment = false
9599
tree
96100

97101
override def transformLiteral(tree: Literal)(using Context): tree.type =
@@ -113,13 +117,10 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
113117
ctx
114118

115119
override def prepareForAssign(tree: Assign)(using Context): Context =
116-
tree.lhs.putAttachment(Ignore, ()) // don't take LHS reference as a read
120+
tree.lhs.putAttachment(AssignmentTarget, ()) // don't take LHS reference as a read
117121
ctx
118122
override def transformAssign(tree: Assign)(using Context): tree.type =
119-
tree.lhs.removeAttachment(Ignore)
120-
val sym = tree.lhs.symbol
121-
if sym.exists then
122-
refInfos.asss.addOne(sym)
123+
tree.lhs.removeAttachment(AssignmentTarget)
123124
tree
124125

125126
override def prepareForMatch(tree: Match)(using Context): Context =
@@ -276,7 +277,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
276277
// if sym is not an enclosing element, record the reference
277278
def refUsage(sym: Symbol)(using Context): Unit =
278279
if !ctx.outersIterator.exists(cur => cur.owner eq sym) then
279-
refInfos.refs.addOne(sym)
280+
refInfos.addRef(sym)
280281

281282
/** Look up a reference in enclosing contexts to determine whether it was introduced by a definition or import.
282283
* The binding of highest precedence must then be correct.
@@ -335,7 +336,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
335336
case none =>
336337

337338
// Avoid spurious NoSymbol and also primary ctors which are never warned about.
338-
// Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
339+
// Selections C.this.toString should be already excluded, but backstopped here for eq, etc.
339340
if !sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
340341

341342
// Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
@@ -404,7 +405,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
404405
candidate = cur
405406
end while
406407
// record usage and possibly an import
407-
refInfos.refs.addOne(sym)
408+
refInfos.addRef(sym)
408409
if candidate != NoContext && candidate.isImportContext && importer != null then
409410
refInfos.sels.put(importer, ())
410411
// possibly record that we have performed this look-up
@@ -443,6 +444,9 @@ object CheckUnused:
443444
/** Ignore reference. */
444445
val Ignore = Property.StickyKey[Unit]
445446

447+
/** Tree is LHS of Assign. */
448+
val AssignmentTarget = Property.StickyKey[Unit]
449+
446450
class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper")
447451

448452
class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining")
@@ -487,6 +491,14 @@ object CheckUnused:
487491

488492
val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code (expansions)
489493
var inliners = 0 // depth of inline def (not inlined yet)
494+
495+
// instead of refs.addOne, use addRef to distinguish a read from a write to var
496+
var isAssignment = false
497+
def addRef(sym: Symbol): Unit =
498+
if isAssignment then
499+
asss.addOne(sym)
500+
else
501+
refs.addOne(sym)
490502
end RefInfos
491503

492504
// Symbols already resolved in the given Context (with name and prefix of lookup).

Diff for: tests/warn/unused-privates.scala

+8
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,11 @@ object `i19998 refinement`:
308308
object `patvar is assignable`:
309309
private var (i, j) = (42, 27) // no warn patvars under -Wunused:privates
310310
println((i, j))
311+
312+
object `i22970 assign lhs was ignored`:
313+
object X:
314+
var global = 0
315+
object Main:
316+
import X.global
317+
def main(args: Array[String]): Unit =
318+
global = 1

0 commit comments

Comments
 (0)