diff --git a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala index 61561d5eaa24..730149381487 100644 --- a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala +++ b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala @@ -60,19 +60,19 @@ object NavigateAST { * the given `span`. */ def untypedPath(span: Span)(using Context): List[Positioned] = - pathTo(span, ctx.compilationUnit.untpdTree) + pathTo(span, List(ctx.compilationUnit.untpdTree)) - /** The reverse path from node `from` to the node that closest encloses `span`, + /** The reverse path from any node in `from` to the node that closest encloses `span`, * or `Nil` if no such path exists. If a non-empty path is returned it starts with - * the node closest enclosing `span` and ends with `from`. + * the node closest enclosing `span` and ends with one of the nodes in `from`. * * @param skipZeroExtent If true, skip over zero-extent nodes in the search. These nodes * do not correspond to code the user wrote since their start and * end point are the same, so this is useful when trying to reconcile * nodes with source code. */ - def pathTo(span: Span, from: Positioned, skipZeroExtent: Boolean = false)(using Context): List[Positioned] = { + def pathTo(span: Span, from: List[Positioned], skipZeroExtent: Boolean = false)(using Context): List[Positioned] = { def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = { var bestFit: List[Positioned] = path while (it.hasNext) { @@ -120,6 +120,6 @@ object NavigateAST { case _ => path } } - singlePath(from, Nil) + childPath(from.iterator, Nil) } } diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 20a746ceb5ac..cb41650c0b57 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -252,14 +252,14 @@ object Interactive { * the tree closest enclosing `pos` and ends with an element of `trees`. */ def pathTo(trees: List[SourceTree], pos: SourcePosition)(using Context): List[Tree] = - trees.find(_.pos.contains(pos)) match { - case Some(tree) => pathTo(tree.tree, pos.span) - case None => Nil - } + pathTo(trees.map(_.tree), pos.span) def pathTo(tree: Tree, span: Span)(using Context): List[Tree] = - if (tree.span.contains(span)) - NavigateAST.pathTo(span, tree, skipZeroExtent = true) + pathTo(List(tree), span) + + private def pathTo(trees: List[Tree], span: Span)(using Context): List[Tree] = + if (trees.exists(_.span.contains(span))) + NavigateAST.pathTo(span, trees, skipZeroExtent = true) .collect { case t: untpd.Tree => t } .dropWhile(!_.hasType).asInstanceOf[List[tpd.Tree]] else Nil diff --git a/language-server/test/dotty/tools/languageserver/HoverTest.scala b/language-server/test/dotty/tools/languageserver/HoverTest.scala index 868134d6ea21..b396f1a15044 100644 --- a/language-server/test/dotty/tools/languageserver/HoverTest.scala +++ b/language-server/test/dotty/tools/languageserver/HoverTest.scala @@ -222,4 +222,13 @@ class HoverTest { |""".withSource .hover(m1 to m2, hoverContent("example.SimpleEnum.Color")) } + + @Test def enums: Unit = { + code"""|package example + |enum TestEnum3: + | case ${m1}A${m2} // no tooltip + | + |""".withSource + .hover(m1 to m2, hoverContent("example.TestEnum3")) + } }