Skip to content

Commit

Permalink
Fix goto-def on exported forwarders (#19494)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Jan 31, 2024
2 parents 62f0324 + 0ee03ef commit 409d13a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 21 deletions.
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
buf.toList
}

def collectSubTrees[A](f: PartialFunction[Tree, A])(using Context): List[A] =
val buf = mutable.ListBuffer[A]()
foreachSubTree(f.runWith(buf += _)(_))
buf.toList

/** Set this tree as the `defTree` of its symbol and return this tree */
def setDefTree(using Context): ThisTree = {
val sym = tree.symbol
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ object Flags {
val (Enum @ _, EnumVal @ _, _) = newFlags(40, "enum")

/** An export forwarder */
val (Exported @ _, _, _) = newFlags(41, "exported")
val (Exported @ _, ExportedTerm @ _, ExportedType @ _) = newFlags(41, "exported")

/** Labeled with `erased` modifier (erased value or class) */
val (Erased @ _, _, _) = newFlags(42, "erased")
Expand Down
19 changes: 15 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ import DenotTransformers.*
import StdNames.*
import NameOps.*
import NameKinds.LazyImplicitName
import ast.tpd
import tpd.{Tree, TreeProvider, TreeOps}
import ast.TreeTypeMap
import ast.*, tpd.*
import Constants.Constant
import Variances.Variance
import reporting.Message
Expand Down Expand Up @@ -325,13 +323,26 @@ object Symbols extends SymUtils {

/** A symbol related to `sym` that is defined in source code.
*
* @see enclosingSourceSymbols
* @see [[interactive.Interactive.enclosingSourceSymbols]]
*/
@annotation.tailrec final def sourceSymbol(using Context): Symbol =
if (!denot.exists)
this
else if (denot.is(ModuleVal))
this.moduleClass.sourceSymbol // The module val always has a zero-extent position
else if denot.is(ExportedType) then
denot.info.dropAlias.finalResultType.typeConstructor match
case tp: NamedType => tp.symbol.sourceSymbol
case _ => this
else if denot.is(ExportedTerm) then
val root = denot.maybeOwner match
case cls: ClassSymbol => cls.rootTreeContaining(name.toString)
case _ => EmptyTree
val targets = root.collectSubTrees:
case tree: DefDef if tree.symbol == denot.symbol => methPart(tree.rhs).tpe
targets.match
case (tp: NamedType) :: _ => tp.symbol.sourceSymbol
case _ => this
else if (denot.is(Synthetic)) {
val linked = denot.linkedClass
if (linked.exists && !linked.is(Synthetic))
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/interactive/SourceTree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ case class SourceTree(tree: tpd.Import | tpd.NameTree, source: SourceFile) {
(treeSpan.end - nameLength, treeSpan.end)
Span(start, end, start)
}
source.atSpan(position)
// Don't widen the span, only narrow.
// E.g. The star in a wildcard export is 1 character,
// and that is the span of the type alias that results from it
// but the name may very well be larger, which we don't want.
val span1 = if treeSpan.contains(position) then position else treeSpan
source.atSpan(span1)
}
case _ =>
NoSourcePosition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package dotty.tools.pc
package dotty.tools
package pc

import scala.annotation.tailrec

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.StdNames
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.Type
import dotty.tools.dotc.interactive.SourceTree
import dotty.tools.dotc.util.SourceFile
import dotty.tools.dotc.util.SourcePosition
import dotc.*
import ast.*, tpd.*
import core.*, Contexts.*, Decorators.*, Flags.*, Names.*, Symbols.*, Types.*
import interactive.*
import util.*
import util.SourcePosition

object MetalsInteractive:

Expand Down Expand Up @@ -205,7 +200,10 @@ object MetalsInteractive:
Nil

case path @ head :: tail =>
if head.symbol.is(Synthetic) then
if head.symbol.is(Exported) then
val sym = head.symbol.sourceSymbol
List((sym, sym.info))
else if head.symbol.is(Synthetic) then
enclosingSymbolsWithExpressionType(
tail,
pos,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class PcDefinitionProvider(
val isLocal = sym.source == pos.source
if isLocal then
val defs =
Interactive.findDefinitions(List(sym), driver, false, false)
Interactive.findDefinitions(List(sym), driver, false, false).filter(_.source == sym.source)
defs.headOption match
case Some(srcTree) =>
val pos = srcTree.namePos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,81 @@ class PcDefinitionSuite extends BasePcDefinitionSuite:
|""".stripMargin
)

@Test def exportType0 =
check(
"""object Foo:
| trait <<Cat>>
|object Bar:
| export Foo.*
|class Test:
| import Bar.*
| def test = new Ca@@t {}
|""".stripMargin
)

@Test def exportType1 =
check(
"""object Foo:
| trait <<Cat>>[A]
|object Bar:
| export Foo.*
|class Test:
| import Bar.*
| def test = new Ca@@t[Int] {}
|""".stripMargin
)

@Test def exportTerm0Nullary =
check(
"""trait Foo:
| def <<meth>>: Int
|class Bar(val foo: Foo):
| export foo.*
| def test(bar: Bar) = bar.me@@th
|""".stripMargin
)

@Test def exportTerm0 =
check(
"""trait Foo:
| def <<meth>>(): Int
|class Bar(val foo: Foo):
| export foo.*
| def test(bar: Bar) = bar.me@@th()
|""".stripMargin
)

@Test def exportTerm1 =
check(
"""trait Foo:
| def <<meth>>(x: Int): Int
|class Bar(val foo: Foo):
| export foo.*
| def test(bar: Bar) = bar.me@@th(0)
|""".stripMargin
)

@Test def exportTerm1Poly =
check(
"""trait Foo:
| def <<meth>>[A](x: A): A
|class Bar(val foo: Foo):
| export foo.*
| def test(bar: Bar) = bar.me@@th(0)
|""".stripMargin
)

@Test def exportTerm1Overload =
check(
"""trait Foo:
| def <<meth>>(x: Int): Int
| def meth(x: String): String
|class Bar(val foo: Foo):
| export foo.*
| def test(bar: Bar) = bar.me@@th(0)
|""".stripMargin
)

@Test def `named-arg-local` =
check(
"""|
Expand Down

0 comments on commit 409d13a

Please sign in to comment.