From 4641ad1f761064bb6b9c2635386d01f7002b3c10 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 31 Jul 2025 17:57:33 +0200 Subject: [PATCH 1/2] fix: Fix extracting refinements from intersection types in dynamic select hovers Co-Authored-By: Bulby <26726264+thedrawingcoder-gamer@users.noreply.github.com> --- .../main/dotty/tools/pc/HoverProvider.scala | 15 +++-- .../tools/pc/tests/hover/HoverTermSuite.scala | 62 +++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index c55a8a0210be..5c6b166e0538 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -13,6 +13,7 @@ import scala.meta.pc.SymbolSearch import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.* import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.* import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.* @@ -221,12 +222,16 @@ object HoverProvider: findRefinement(parent) case _ => None - val refTpe = sel.typeOpt.widen.deepDealiasAndSimplify match - case r: RefinedType => Some(r) - case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.deepDealiasAndSimplify) - case _ => None + def extractRefinements(t: Type): List[Type] = t match + case r: RefinedType => List(r) + case t: TypeRef => extractRefinements(t.deepDealiasAndSimplify) + case t: (TermRef | TypeProxy) => List(t.termSymbol.info.deepDealiasAndSimplify) + case AndType(l , r) => List(extractRefinements(l), extractRefinements(r)).flatten + case _ => Nil - refTpe.flatMap(findRefinement).asJava + val refTpe: List[Type] = extractRefinements(sel.typeOpt.widen) + + refTpe.flatMap(findRefinement).headOption.asJava case _ => ju.Optional.empty().nn diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala index c483dc289b0e..525e5f393ef4 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala @@ -851,3 +851,65 @@ class HoverTermSuite extends BaseHoverSuite: |""".stripMargin, "val thisIsAVeryLongName: Int".hover ) + + @Test def `intersection_of_selectable-1` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.a@@a + |""".stripMargin, + "val aa: Int".hover + ) + + @Test def `intersection_of_selectable-2` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val aa: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.a@@a + |""".stripMargin, + "val aa: Int & String".hover + ) + + @Test def `intersection_of_selectable-3` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.b@@b + |""".stripMargin, + "val bb: String".hover + ) + + @Test def `intersection_of_selectable-4` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type C = Record { val cc: Float } + |type AB = A & B + |type ABC = AB & C + | + |val abc: ABC = Record().asInstanceOf[ABC] + |val abc_a = abc.a@@a + |""".stripMargin, + "val aa: Int".hover + ) From e593512c8d35f0b8c930bb6b16bbd7157c3d9c4e Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Sun, 3 Aug 2025 18:28:54 +0200 Subject: [PATCH 2/2] Also dealias applied types when looking for refinement types for hover info --- .../src/main/dotty/tools/pc/HoverProvider.scala | 11 ++++++++--- .../dotty/tools/pc/tests/hover/HoverTermSuite.scala | 13 +++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index 5c6b166e0538..375a75d0307f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -224,12 +224,17 @@ object HoverProvider: def extractRefinements(t: Type): List[Type] = t match case r: RefinedType => List(r) - case t: TypeRef => extractRefinements(t.deepDealiasAndSimplify) - case t: (TermRef | TypeProxy) => List(t.termSymbol.info.deepDealiasAndSimplify) + case t: (TypeRef | AppliedType) => + // deepDealiasAndSimplify can succeed with no progress, so we have to avoid infinite loops + val t1 = t.deepDealiasAndSimplify + if t1 == t then Nil + else extractRefinements(t1) + case t: TermRef => extractRefinements(t.widen) + case t: TypeProxy => List(t.termSymbol.info.deepDealiasAndSimplify) case AndType(l , r) => List(extractRefinements(l), extractRefinements(r)).flatten case _ => Nil - val refTpe: List[Type] = extractRefinements(sel.typeOpt.widen) + val refTpe: List[Type] = extractRefinements(sel.typeOpt) refTpe.flatMap(findRefinement).headOption.asJava case _ => diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala index 525e5f393ef4..60827f1e3590 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala @@ -913,3 +913,16 @@ class HoverTermSuite extends BaseHoverSuite: |""".stripMargin, "val aa: Int".hover ) + + @Test def `intersection_of_selectable-5` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type AL = List[Int] & Record { val aa: Int } + | + |val al: AL = ???.asInstanceOf[ABC] + |val al_a = al.a@@a + |""".stripMargin, + "val aa: Int".hover + )