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

IDE: Support jump to definition in imports #4199

Merged
merged 21 commits into from
Nov 16, 2018
Merged
Show file tree
Hide file tree
Changes from 20 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
84 changes: 84 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1147,5 +1147,89 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case _ =>
EmptyTree
}

/**
* The symbols that are imported with `expr.name`
*
* @param expr The base of the import statement
* @param name The name that is being imported.
* @return All the symbols that would be imported with `expr.name`.
*/
def importedSymbols(expr: Tree, name: Name)(implicit ctx: Context): List[Symbol] = {
def lookup(name: Name): Symbol = expr.tpe.member(name).symbol
val symbols =
List(lookup(name.toTermName),
lookup(name.toTypeName),
lookup(name.moduleClassName),
lookup(name.sourceModuleName))

symbols.map(_.sourceSymbol).filter(_.exists).distinct
}

/**
* All the symbols that are imported by the first selector of `imp` that matches
* `selectorPredicate`.
*
* @param imp The import statement to analyze
* @param selectorPredicate A test to find the selector to use.
* @return The symbols imported.
*/
def importedSymbols(imp: Import,
selectorPredicate: untpd.Tree => Boolean = util.common.alwaysTrue)
(implicit ctx: Context): List[Symbol] = {
imp.selectors.find(selectorPredicate) match {
case Some(id: untpd.Ident) =>
importedSymbols(imp.expr, id.name)
case Some(Thicket((id: untpd.Ident) :: (_: untpd.Ident) :: Nil)) =>
importedSymbols(imp.expr, id.name)
case _ =>
Nil
}
}

/**
* The list of select trees that resolve to the same symbols as the ones that are imported
* by `imp`.
*/
def importSelections(imp: Import)(implicit ctx: Context): List[Select] = {
Duhemm marked this conversation as resolved.
Show resolved Hide resolved
def imported(sym: Symbol, id: untpd.Ident, rename: Option[untpd.Ident]): List[Select] = {
val noPosExpr = focusPositions(imp.expr)
val selectTree = Select(noPosExpr, sym.name).withPos(id.pos)
rename match {
case None =>
selectTree :: Nil
case Some(rename) =>
// Get the type of the symbol that is actually selected, and construct a select
// node with the new name and the type of the real symbol.
val name = if (sym.name.isTypeName) rename.name.toTypeName else rename.name
val actual = Select(noPosExpr, sym.name)
val renameTree = Select(noPosExpr, name).withPos(rename.pos).withType(actual.tpe)
selectTree :: renameTree :: Nil
}
}

imp.selectors.flatMap {
case Ident(nme.WILDCARD) =>
Nil
case id: untpd.Ident =>
importedSymbols(imp.expr, id.name).flatMap { sym =>
imported(sym, id, None)
}
case Thicket((id: untpd.Ident) :: (newName: untpd.Ident) :: Nil) =>
importedSymbols(imp.expr, id.name).flatMap { sym =>
imported(sym, id, Some(newName))
}
}
}

/** Replaces all positions in `tree` with zero-extent positions */
private def focusPositions(tree: Tree)(implicit ctx: Context): Tree = {
Duhemm marked this conversation as resolved.
Show resolved Hide resolved
val transformer = new tpd.TreeMap {
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
super.transform(tree).withPos(tree.pos.focus)
}
}
transformer.transform(tree)
}
}

20 changes: 20 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,26 @@ object Symbols {
}
}

/** A symbol related to `sym` that is defined in source code.
*
* @see enclosingSourceSymbols
*/
@annotation.tailrec final def sourceSymbol(implicit ctx: 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(Synthetic)) {
val linked = denot.linkedClass
if (linked.exists && !linked.is(Synthetic))
linked
else
denot.owner.sourceSymbol
}
else if (denot.isPrimaryConstructor)
denot.owner.sourceSymbol
else this

/** The position of this symbol, or NoPosition if the symbol was not loaded
* from source or from TASTY. This is always a zero-extent position.
*
Expand Down
Loading