Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support go-to-definition on named arguments #5057

Merged
merged 1 commit into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/interactive/Interactive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@ object Interactive {
else path.head.tpe
}

/** The closest enclosing tree with a symbol containing position `pos`.
/** The closest enclosing tree with a symbol containing position `pos`, or the `EmptyTree`.
*/
def enclosingTree(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): Tree =
pathTo(trees, pos).dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree)
enclosingTree(pathTo(trees, pos))

/** The closes enclosing tree with a symbol, or the `EmptyTree`.
*/
def enclosingTree(path: List[Tree])(implicit ctx: Context): Tree =
path.dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree)

/** The source symbol of the closest enclosing tree with a symbol containing position `pos`.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,31 @@ class DottyLanguageServer extends LanguageServer
implicit val ctx = driver.currentCtx

val pos = sourcePosition(driver, uri, params.getPosition)
val enclTree = Interactive.enclosingTree(driver.openedTrees(uri), pos)
val sym = Interactive.sourceSymbol(enclTree.symbol)
val path = Interactive.pathTo(driver.openedTrees(uri), pos)
val enclTree = Interactive.enclosingTree(path)

val sym = {
val sym = path match {
// For a named arg, find the target `DefDef` and jump to the param
case NamedArg(name, _) :: Apply(fn, _) :: _ =>
val funSym = fn.symbol
if (funSym.name == StdNames.nme.copy
&& funSym.is(Synthetic)
&& funSym.owner.is(CaseClass)) {
funSym.owner.info.member(name).symbol
} else {
val classTree = funSym.topLevelClass.asClass.tree
tpd.defPath(funSym, classTree).lastOption.flatMap {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expression is a bit dense. I believe the following would be slightly longer, but more readable:

val paramSymbol = 
  for {
    DefDef(_, _, paramss, _, _) <- tpd.defPath(funSym, classTree).lastOption
    param <- paramss.flatten.find(_.name == name)
  } yield param.symbol
paramSymbol.getOrElse(fn.symbol)

case DefDef(_, _, paramss, _, _) =>
paramss.flatten.find(_.name == name).map(_.symbol)
}.getOrElse(fn.symbol)
}

case _ =>
enclTree.symbol
}
Interactive.sourceSymbol(sym)
}

if (sym == NoSymbol) Nil.asJava
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dotty.tools.languageserver
import org.junit.Test

import dotty.tools.languageserver.util.Code._
import dotty.tools.languageserver.util.embedded.CodeMarker

class DefinitionTest {

Expand Down Expand Up @@ -43,4 +44,85 @@ class DefinitionTest {
).definition(m3 to m4, List(m1 to m2))
}

@Test def goToDefNamedArg: Unit = {
code"""object Foo {
def foo(${m1}x${m2}: Int) = ${m3}x${m4}
foo(${m5}x${m6} = 2)
}""".withSource
.definition(m1 to m2, List(m1 to m2))
.definition(m3 to m4, List(m1 to m2))
.definition(m5 to m6, List(m1 to m2))
}

@Test def goToDefNamedArgOverload: Unit = {
val m9 = new CodeMarker("m9")
val m10 = new CodeMarker("m10")
val m11 = new CodeMarker("m11")
val m12 = new CodeMarker("m12")
val m13 = new CodeMarker("m13")
val m14 = new CodeMarker("m14")

code"""object Foo {
def foo(${m1}x${m2}: String): String = ${m3}x${m4}
def foo(${m5}x${m6}: Int): String = foo(${m7}x${m8} = ${m9}x${m10}.toString)
foo(${m11}x${m12} = "a")
foo(${m13}x${m14} = 2)
}""".withSource
.definition(m1 to m2, List(m1 to m2))
.definition(m3 to m4, List(m1 to m2))
.definition(m5 to m6, List(m5 to m6))
.definition(m7 to m8, List(m1 to m2))
.definition(m9 to m10, List(m5 to m6))
.definition(m11 to m12, List(m1 to m2))
.definition(m13 to m14, List(m5 to m6))
}

@Test def goToConstructorNamedArg: Unit = {
withSources(
code"""class Foo(${m1}x${m2}: Int)""",
code"""class Bar extends Foo(${m3}x${m4} = 5)""",
code"""object Buzz { new Foo(${m5}x${m6} = 2) }"""
) .definition(m1 to m2, List(m1 to m2))
.definition(m3 to m4, List(m1 to m2))
.definition(m5 to m6, List(m1 to m2))
}

@Test def goToConstructorNamedArgOverload: Unit = {
val m9 = new CodeMarker("m9")
val m10 = new CodeMarker("m10")
val m11 = new CodeMarker("m11")
val m12 = new CodeMarker("m12")

withSources(
code"""class Foo(${m1}x${m2}: String) {
def this(${m3}x${m4}: Int) = this(${m5}x${m6} = ${m7}x${m8}.toString)
}""",
code"""object Bar {
new Foo(${m9}x${m10} = 1)
new Foo(${m11}x${m12} = "a")
}"""
) .definition(m1 to m2, List(m1 to m2))
.definition(m3 to m4, List(m3 to m4))
.definition(m5 to m6, List(m1 to m2))
.definition(m7 to m8, List(m3 to m4))
.definition(m9 to m10, List(m3 to m4))
.definition(m11 to m12, List(m1 to m2))
}

@Test def goToParamCopyMethod: Unit = {
val m9 = new CodeMarker("m9")
val m10 = new CodeMarker("m10")

withSources(
code"""case class Foo(${m1}x${m2}: Int, ${m3}y${m4}: String)""",
code"""object Bar {
Foo(0, "a").copy(${m5}x${m6} = 1, ${m7}y${m8} = "b")
Foo(2, "c").copy(${m9}y${m10} = "d")"""
) .definition(m1 to m2, List(m1 to m2))
.definition(m3 to m4, List(m3 to m4))
.definition(m5 to m6, List(m1 to m2))
.definition(m7 to m8, List(m3 to m4))
.definition(m9 to m10, List(m3 to m4))
}

}