@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, ter
1010import  dotty .tools .dotc .core .NameOps .{isAnonymousFunctionName , isReplWrapperName , 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
@@ -213,6 +223,16 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
213223      refInfos.register(tree)
214224    tree
215225
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+ 
216236  override  def  transformOther (tree : Tree )(using  Context ):  tree.type  = 
217237    tree match 
218238    case  imp : Import  => 
@@ -406,6 +426,38 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
406426    if  candidate !=  NoContext  &&  candidate.isImportContext &&  importer !=  null  then 
407427      refInfos.sels.put(importer, ())
408428  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 
409461end  CheckUnused 
410462
411463object  CheckUnused : 
0 commit comments