@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, ter
1010import dotty .tools .dotc .core .NameOps .{isAnonymousFunctionName , isReplWrapperName , isContextFunction , setterName }
1111import dotty .tools .dotc .core .NameKinds .{
1212 BodyRetainerName , ContextBoundParamName , ContextFunctionParamName , DefaultGetterName , WildcardParamName }
13+ import dotty .tools .dotc .core .Scopes .newScope
1314import dotty .tools .dotc .core .StdNames .nme
1415import dotty .tools .dotc .core .Symbols .{ClassSymbol , NoSymbol , Symbol , defn , isDeprecated , requiredClass , requiredModule }
1516import dotty .tools .dotc .core .Types .*
@@ -19,6 +20,7 @@ import dotty.tools.dotc.rewrites.Rewrites
1920import dotty .tools .dotc .transform .MegaPhase .MiniPhase
2021import dotty .tools .dotc .typer .{ImportInfo , Typer }
2122import dotty .tools .dotc .typer .Deriving .OriginalTypeClass
23+ import dotty .tools .dotc .typer .Implicits .{ContextualImplicits , RenamedImplicitRef }
2224import dotty .tools .dotc .util .{Property , Spans , SrcPos }, Spans .Span
2325import dotty .tools .dotc .util .Chars .{isLineBreakChar , isWhitespace }
2426import dotty .tools .dotc .util .chaining .*
@@ -116,6 +118,14 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
116118 args.foreach(_.withAttachment(ForArtifact , ()))
117119 case _ =>
118120 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
119129
120130 override def prepareForAssign (tree : Assign )(using Context ): Context =
121131 tree.lhs.putAttachment(AssignmentTarget , ()) // don't take LHS reference as a read
@@ -196,6 +206,16 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
196206 refInfos.register(tree)
197207 tree
198208
209+ override def prepareForStats (trees : List [Tree ])(using Context ): Context =
210+ // gather local implicits while ye may
211+ if ! ctx.owner.isClass then
212+ if trees.exists(t => t.isDef && t.symbol.is(Given ) && t.symbol.isLocalToBlock) then
213+ val scope = newScope.openForMutations
214+ for tree <- trees if tree.isDef && tree.symbol.is(Given ) do
215+ scope.enter(tree.symbol.name, tree.symbol)
216+ return ctx.fresh.setScope(scope)
217+ ctx
218+
199219 override def transformOther (tree : Tree )(using Context ): tree.type =
200220 tree match
201221 case imp : Import =>
@@ -374,6 +394,38 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
374394 if candidate != NoContext && candidate.isImportContext && importer != null then
375395 refInfos.sels.put(importer, ())
376396 end resolveUsage
397+
398+ /** Simulate implicit search for contextual implicits in lexical scope and mark any definitions or imports as used.
399+ * Avoid cached ctx.implicits because it needs the precise import context that introduces the given.
400+ */
401+ def resolveScoped (tp : Type )(using Context ): Unit =
402+ var done = false
403+ val ctxs = ctx.outersIterator
404+ while ! done && ctxs.hasNext do
405+ val cur = ctxs.next()
406+ val implicitRefs : List [ImplicitRef ] =
407+ if (cur.isClassDefContext) cur.owner.thisType.implicitMembers
408+ else if (cur.isImportContext) cur.importInfo.nn.importedImplicits
409+ else if (cur.isNonEmptyScopeContext) cur.scope.implicitDecls
410+ else Nil
411+ implicitRefs.find(ref => ref.underlyingRef.widen <:< tp) match
412+ case Some (found : TermRef ) =>
413+ refInfos.addRef(found.denot.symbol)
414+ if cur.isImportContext then
415+ cur.importInfo.nn.selectors.find(sel => sel.isGiven || sel.rename == found.name) match
416+ case Some (sel) =>
417+ refInfos.sels.put(sel, ())
418+ case _ =>
419+ return
420+ case Some (found : RenamedImplicitRef ) if cur.isImportContext =>
421+ refInfos.addRef(found.underlyingRef.denot.symbol)
422+ cur.importInfo.nn.selectors.find(sel => sel.rename == found.implicitName) match
423+ case Some (sel) =>
424+ refInfos.sels.put(sel, ())
425+ case _ =>
426+ return
427+ case _ =>
428+ end resolveScoped
377429end CheckUnused
378430
379431object CheckUnused :
0 commit comments