|
1 | | -package dotty.tools.dotc.transform |
| 1 | +package dotty.tools.dotc |
| 2 | +package transform |
2 | 3 |
|
3 | 4 | import dotty.tools.dotc.ast.desugar.{ForArtifact, PatternVar} |
4 | 5 | import dotty.tools.dotc.ast.tpd.* |
@@ -58,17 +59,16 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha |
58 | 59 | if tree.symbol.exists then |
59 | 60 | // if in an inline expansion, resolve at summonInline (synthetic pos) or in an enclosing call site |
60 | 61 | val resolving = |
61 | | - refInfos.inlined.isEmpty |
62 | | - || tree.srcPos.isZeroExtentSynthetic |
63 | | - || refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos)) |
64 | | - if resolving && !ignoreTree(tree) then |
| 62 | + tree.srcPos.isUserCode |
| 63 | + || tree.srcPos.isZeroExtentSynthetic // take as summonInline |
| 64 | + if !ignoreTree(tree) then |
65 | 65 | def loopOverPrefixes(prefix: Type, depth: Int): Unit = |
66 | 66 | if depth < 10 && prefix.exists && !prefix.classSymbol.isEffectiveRoot then |
67 | | - resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix) |
| 67 | + resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix, imports = resolving) |
68 | 68 | loopOverPrefixes(prefix.normalizedPrefix, depth + 1) |
69 | 69 | if tree.srcPos.isZeroExtentSynthetic then |
70 | 70 | loopOverPrefixes(tree.typeOpt.normalizedPrefix, depth = 0) |
71 | | - resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject) |
| 71 | + resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject, imports = resolving) |
72 | 72 | else if tree.hasType then |
73 | 73 | resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject) |
74 | 74 | refInfos.isAssignment = false |
@@ -160,14 +160,8 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha |
160 | 160 | case _ => |
161 | 161 | tree |
162 | 162 |
|
163 | | - override def prepareForInlined(tree: Inlined)(using Context): Context = |
164 | | - refInfos.inlined.push(tree.call.srcPos) |
165 | | - ctx |
166 | 163 | override def transformInlined(tree: Inlined)(using Context): tree.type = |
167 | | - //transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs |
168 | | - val _ = refInfos.inlined.pop() |
169 | | - if !tree.call.isEmpty && phaseMode.eq(PhaseMode.Aggregate) then |
170 | | - transformAllDeep(tree.call) |
| 164 | + transformAllDeep(tree.call) |
171 | 165 | tree |
172 | 166 |
|
173 | 167 | override def prepareForBind(tree: Bind)(using Context): Context = |
@@ -293,8 +287,11 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha |
293 | 287 | * e.g., in `scala.Int`, `scala` is in scope for typer, but here we reverse-engineer the attribution. |
294 | 288 | * For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`. |
295 | 289 | * For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence. |
| 290 | + * |
| 291 | + * The `imports` flag is whether an identifier can mark an import as used: the flag is false |
| 292 | + * for inlined code, except for `summonInline` (and related constructs) which are resolved at inlining. |
296 | 293 | */ |
297 | | - def resolveUsage(sym0: Symbol, name: Name, prefix: Type)(using Context): Unit = |
| 294 | + def resolveUsage(sym0: Symbol, name: Name, prefix: Type, imports: Boolean = true)(using Context): Unit = |
298 | 295 | import PrecedenceLevels.* |
299 | 296 | val sym = sym0.userSymbol |
300 | 297 |
|
@@ -398,7 +395,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha |
398 | 395 | // record usage and possibly an import |
399 | 396 | if !enclosed then |
400 | 397 | refInfos.addRef(sym) |
401 | | - if candidate != NoContext && candidate.isImportContext && importer != null then |
| 398 | + if imports && candidate != NoContext && candidate.isImportContext && importer != null then |
402 | 399 | refInfos.sels.put(importer, ()) |
403 | 400 | end resolveUsage |
404 | 401 |
|
@@ -470,7 +467,7 @@ object CheckUnused: |
470 | 467 | val nowarn = mutable.Set.empty[Symbol] // marked @nowarn |
471 | 468 | val imps = new IdentityHashMap[Import, Unit] // imports |
472 | 469 | val sels = new IdentityHashMap[ImportSelector, Unit] // matched selectors |
473 | | - def register(tree: Tree)(using Context): Unit = if inlined.isEmpty then |
| 470 | + def register(tree: Tree)(using Context): Unit = if tree.srcPos.isUserCode then |
474 | 471 | tree match |
475 | 472 | case imp: Import => |
476 | 473 | if inliners == 0 |
@@ -499,7 +496,6 @@ object CheckUnused: |
499 | 496 | if tree.symbol ne NoSymbol then |
500 | 497 | defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path |
501 | 498 |
|
502 | | - val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code (expansions) |
503 | 499 | var inliners = 0 // depth of inline def (not inlined yet) |
504 | 500 |
|
505 | 501 | // instead of refs.addOne, use addRef to distinguish a read from a write to var |
@@ -1010,6 +1006,10 @@ object CheckUnused: |
1010 | 1006 | extension (pos: SrcPos) |
1011 | 1007 | def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.isZeroExtent |
1012 | 1008 | def isSynthetic: Boolean = pos.span.isSynthetic && pos.span.exists |
| 1009 | + def isUserCode(using Context): Boolean = |
| 1010 | + val inlineds = enclosingInlineds // per current context |
| 1011 | + inlineds.isEmpty |
| 1012 | + || inlineds.last.srcPos.sourcePos.contains(pos.sourcePos) |
1013 | 1013 |
|
1014 | 1014 | extension [A <: AnyRef](arr: Array[A]) |
1015 | 1015 | // returns `until` if not satisfied |
|
0 commit comments