@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, ter
10
10
import dotty .tools .dotc .core .NameOps .{isAnonymousFunctionName , isReplWrapperName , setterName }
11
11
import dotty .tools .dotc .core .NameKinds .{
12
12
BodyRetainerName , ContextBoundParamName , ContextFunctionParamName , DefaultGetterName , WildcardParamName }
13
+ import dotty .tools .dotc .core .Scopes .newScope
13
14
import dotty .tools .dotc .core .StdNames .nme
14
15
import dotty .tools .dotc .core .Symbols .{ClassSymbol , NoSymbol , Symbol , defn , isDeprecated , requiredClass , requiredModule }
15
16
import dotty .tools .dotc .core .Types .*
@@ -19,6 +20,7 @@ import dotty.tools.dotc.rewrites.Rewrites
19
20
import dotty .tools .dotc .transform .MegaPhase .MiniPhase
20
21
import dotty .tools .dotc .typer .{ImportInfo , Typer }
21
22
import dotty .tools .dotc .typer .Deriving .OriginalTypeClass
23
+ import dotty .tools .dotc .typer .Implicits .{ContextualImplicits , RenamedImplicitRef }
22
24
import dotty .tools .dotc .util .{Property , Spans , SrcPos }, Spans .Span
23
25
import dotty .tools .dotc .util .Chars .{isLineBreakChar , isWhitespace }
24
26
import dotty .tools .dotc .util .chaining .*
@@ -116,6 +118,14 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
116
118
args.foreach(_.withAttachment(ForArtifact , ()))
117
119
case _ =>
118
120
ctx
121
+ override def transformApply (tree : Apply )(using Context ): tree.type =
122
+ // check for multiversal equals
123
+ tree match
124
+ case Apply (Select (left, nme.Equals | nme.NotEquals ), right :: Nil ) =>
125
+ val caneq = defn.CanEqualClass .typeRef.appliedTo(left.tpe.widen :: right.tpe.widen :: Nil )
126
+ resolveScoped(caneq)
127
+ case _ =>
128
+ tree
119
129
120
130
override def prepareForAssign (tree : Assign )(using Context ): Context =
121
131
tree.lhs.putAttachment(AssignmentTarget , ()) // don't take LHS reference as a read
@@ -213,6 +223,16 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
213
223
refInfos.register(tree)
214
224
tree
215
225
226
+ override def prepareForStats (trees : List [Tree ])(using Context ): Context =
227
+ // gather local implicits while ye may
228
+ if ! ctx.owner.isClass then
229
+ if trees.exists(t => t.isDef && t.symbol.is(Given ) && t.symbol.isLocalToBlock) then
230
+ val scope = newScope.openForMutations
231
+ for tree <- trees if tree.isDef && tree.symbol.is(Given ) do
232
+ scope.enter(tree.symbol.name, tree.symbol)
233
+ return ctx.fresh.setScope(scope)
234
+ ctx
235
+
216
236
override def transformOther (tree : Tree )(using Context ): tree.type =
217
237
tree match
218
238
case imp : Import =>
@@ -406,6 +426,38 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
406
426
if candidate != NoContext && candidate.isImportContext && importer != null then
407
427
refInfos.sels.put(importer, ())
408
428
end resolveUsage
429
+
430
+ /** Simulate implicit search for contextual implicits in lexical scope and mark any definitions or imports as used.
431
+ * Avoid cached ctx.implicits because it needs the precise import context that introduces the given.
432
+ */
433
+ def resolveScoped (tp : Type )(using Context ): Unit =
434
+ var done = false
435
+ val ctxs = ctx.outersIterator
436
+ while ! done && ctxs.hasNext do
437
+ val cur = ctxs.next()
438
+ val implicitRefs : List [ImplicitRef ] =
439
+ if (cur.isClassDefContext) cur.owner.thisType.implicitMembers
440
+ else if (cur.isImportContext) cur.importInfo.nn.importedImplicits
441
+ else if (cur.isNonEmptyScopeContext) cur.scope.implicitDecls
442
+ else Nil
443
+ implicitRefs.find(ref => ref.underlyingRef.widen <:< tp) match
444
+ case Some (found : TermRef ) =>
445
+ refInfos.addRef(found.denot.symbol)
446
+ if cur.isImportContext then
447
+ cur.importInfo.nn.selectors.find(sel => sel.isGiven || sel.rename == found.name) match
448
+ case Some (sel) =>
449
+ refInfos.sels.put(sel, ())
450
+ case _ =>
451
+ return
452
+ case Some (found : RenamedImplicitRef ) if cur.isImportContext =>
453
+ refInfos.addRef(found.underlyingRef.denot.symbol)
454
+ cur.importInfo.nn.selectors.find(sel => sel.rename == found.implicitName) match
455
+ case Some (sel) =>
456
+ refInfos.sels.put(sel, ())
457
+ case _ =>
458
+ return
459
+ case _ =>
460
+ end resolveScoped
409
461
end CheckUnused
410
462
411
463
object CheckUnused :
0 commit comments