@@ -8,7 +8,7 @@ import scala.collection._
8
8
import ast .{NavigateAST , Trees , tpd , untpd }
9
9
import core ._ , core .Decorators .{sourcePos => _ , _ }
10
10
import Contexts ._ , Flags ._ , Names ._ , NameOps ._ , Symbols ._ , Trees ._ , Types ._
11
- import util .Positions ._ , util .SourcePosition
11
+ import util .Positions ._ , util .SourceFile , util . SourcePosition
12
12
import core .Denotations .SingleDenotation
13
13
import NameKinds .SimpleNameKind
14
14
import config .Printers .interactiv
@@ -28,6 +28,7 @@ object Interactive {
28
28
val references : Int = 4 // include references
29
29
val definitions : Int = 8 // include definitions
30
30
val linkedClass : Int = 16 // include `symbol.linkedClass`
31
+ val imports : Int = 32 // include imports in the results
31
32
}
32
33
33
34
/** Does this tree define a symbol ? */
@@ -59,54 +60,45 @@ object Interactive {
59
60
*
60
61
* @see sourceSymbol
61
62
*/
62
- def enclosingSourceSymbol (path : List [Tree ])(implicit ctx : Context ): Symbol = {
63
- val sym = path match {
63
+ def enclosingSourceSymbols (path : List [Tree ], pos : SourcePosition )(implicit ctx : Context ): List [ Symbol ] = {
64
+ val syms = path match {
64
65
// For a named arg, find the target `DefDef` and jump to the param
65
66
case NamedArg (name, _) :: Apply (fn, _) :: _ =>
66
67
val funSym = fn.symbol
67
68
if (funSym.name == StdNames .nme.copy
68
69
&& funSym.is(Synthetic )
69
70
&& funSym.owner.is(CaseClass )) {
70
- funSym.owner.info.member(name).symbol
71
+ funSym.owner.info.member(name).symbol :: Nil
71
72
} else {
72
73
val classTree = funSym.topLevelClass.asClass.rootTree
73
74
val paramSymbol =
74
75
for {
75
76
DefDef (_, _, paramss, _, _) <- tpd.defPath(funSym, classTree).lastOption
76
77
param <- paramss.flatten.find(_.name == name)
77
78
} yield param.symbol
78
- paramSymbol.getOrElse(fn.symbol)
79
+ paramSymbol.getOrElse(fn.symbol) :: Nil
79
80
}
80
81
81
82
// For constructor calls, return the `<init>` that was selected
82
83
case _ :: (_ : New ) :: (select : Select ) :: _ =>
83
- select.symbol
84
+ select.symbol :: Nil
85
+
86
+ case (_ : Thicket ) :: (imp : Import ) :: _ =>
87
+ importedSymbols(imp, _.pos.contains(pos.pos))
88
+
89
+ case (imp : Import ) :: _ =>
90
+ importedSymbols(imp, _.pos.contains(pos.pos))
84
91
85
92
case _ =>
86
- enclosingTree(path).symbol
93
+ enclosingTree(path).symbol :: Nil
87
94
}
88
- Interactive .sourceSymbol(sym)
89
- }
90
95
91
- /**
92
- * The source symbol that is the closest to the path to `pos` in `trees`.
93
- *
94
- * Computes the path from the tree with position `pos` in `trees`, and extract it source
95
- * symbol.
96
- *
97
- * @param trees The trees in which to look for a path to `pos`.
98
- * @param pos That target position of the path.
99
- * @return The source symbol that is the closest to the computed path.
100
- *
101
- * @see sourceSymbol
102
- */
103
- def enclosingSourceSymbol (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): Symbol = {
104
- enclosingSourceSymbol(pathTo(trees, pos))
96
+ syms.map(Interactive .sourceSymbol).filter(_.exists)
105
97
}
106
98
107
99
/** A symbol related to `sym` that is defined in source code.
108
100
*
109
- * @see enclosingSourceSymbol
101
+ * @see enclosingSourceSymbols
110
102
*/
111
103
@ tailrec def sourceSymbol (sym : Symbol )(implicit ctx : Context ): Symbol =
112
104
if (! sym.exists)
@@ -304,32 +296,60 @@ object Interactive {
304
296
* source code.
305
297
*/
306
298
def namedTrees (trees : List [SourceTree ], include : Include .Set , sym : Symbol )
307
- (implicit ctx : Context ): List [SourceTree ] =
299
+ (implicit ctx : Context ): List [SourceNamedTree ] =
308
300
if (! sym.exists)
309
301
Nil
310
302
else
311
- namedTrees(trees, ( include & Include .references) != 0 , matchSymbol(_, sym, include))
303
+ namedTrees(trees, include, matchSymbol(_, sym, include))
312
304
313
305
/** Find named trees with a non-empty position whose name contains `nameSubstring` in `trees`.
314
306
*/
315
307
def namedTrees (trees : List [SourceTree ], nameSubstring : String )
316
- (implicit ctx : Context ): List [SourceTree ] = {
308
+ (implicit ctx : Context ): List [SourceNamedTree ] = {
317
309
val predicate : NameTree => Boolean = _.name.toString.contains(nameSubstring)
318
- namedTrees(trees, includeReferences = false , predicate)
310
+ namedTrees(trees, 0 , predicate)
319
311
}
320
312
321
313
/** Find named trees with a non-empty position satisfying `treePredicate` in `trees`.
322
314
*
323
315
* @param includeReferences If true, include references and not just definitions
324
316
*/
325
- def namedTrees (trees : List [SourceTree ], includeReferences : Boolean , treePredicate : NameTree => Boolean )
326
- (implicit ctx : Context ): List [SourceTree ] = safely {
327
- val buf = new mutable.ListBuffer [SourceTree ]
317
+ def namedTrees (trees : List [SourceTree ], include : Include .Set , treePredicate : NameTree => Boolean )
318
+ (implicit ctx : Context ): List [SourceNamedTree ] = safely {
319
+ val includeReferences = (include & Include .references) != 0
320
+ val includeImports = (include & Include .imports) != 0
321
+ val buf = new mutable.ListBuffer [SourceNamedTree ]
328
322
329
- trees foreach { case SourceTree (topTree, source) =>
323
+ def traverser ( source : SourceFile ) = {
330
324
new untpd.TreeTraverser {
325
+ private def handleImport (imported : List [Symbol ],
326
+ uexpr : untpd.Tree ,
327
+ id : untpd.Ident ,
328
+ rename : Option [untpd.Ident ]): Unit = {
329
+ val expr = uexpr.asInstanceOf [tpd.Tree ]
330
+ imported match {
331
+ case Nil =>
332
+ traverse(expr)
333
+ case syms =>
334
+ syms.foreach { sym =>
335
+ val tree = tpd.Select (expr, sym.name).withPos(id.pos)
336
+ val renameTree = rename.map { r =>
337
+ val name = if (sym.name.isTypeName) r.name.toTypeName else r.name
338
+ RenameTree (name, tpd.Select (expr, sym.name)).withPos(r.pos)
339
+ }
340
+ renameTree.foreach(traverse)
341
+ traverse(tree)
342
+ }
343
+ }
344
+ }
331
345
override def traverse (tree : untpd.Tree )(implicit ctx : Context ) = {
332
346
tree match {
347
+ case imp @ Import (uexpr, (id : untpd.Ident ) :: Nil ) if includeImports =>
348
+ val imported = importedSymbols(imp.asInstanceOf [tpd.Import ])
349
+ handleImport(imported, uexpr, id, None )
350
+ case imp @ Import (uexpr, Thicket ((id : untpd.Ident ) :: (rename : untpd.Ident ) :: Nil ) :: Nil ) if includeImports =>
351
+ val imported = importedSymbols(imp.asInstanceOf [tpd.Import ])
352
+ handleImport(imported, uexpr, id, Some (rename))
333
353
case utree : untpd.NameTree if tree.hasType =>
334
354
val tree = utree.asInstanceOf [tpd.NameTree ]
335
355
if (tree.symbol.exists
@@ -338,17 +358,19 @@ object Interactive {
338
358
&& ! tree.pos.isZeroExtent
339
359
&& (includeReferences || isDefinition(tree))
340
360
&& treePredicate(tree))
341
- buf += SourceTree (tree, source)
361
+ buf += SourceNamedTree (tree, source)
342
362
traverseChildren(tree)
343
363
case tree : untpd.Inlined =>
344
364
traverse(tree.call)
345
365
case _ =>
346
366
traverseChildren(tree)
347
367
}
348
368
}
349
- }.traverse(topTree)
369
+ }
350
370
}
351
371
372
+ trees.foreach(t => traverser(t.source).traverse(t.tree))
373
+
352
374
buf.toList
353
375
}
354
376
@@ -361,9 +383,8 @@ object Interactive {
361
383
*/
362
384
def findTreesMatching (trees : List [SourceTree ],
363
385
includes : Include .Set ,
364
- symbol : Symbol )(implicit ctx : Context ): List [SourceTree ] = {
386
+ symbol : Symbol )(implicit ctx : Context ): List [SourceNamedTree ] = {
365
387
val linkedSym = symbol.linkedClass
366
- val includeReferences = (includes & Include .references) != 0
367
388
val includeDeclaration = (includes & Include .definitions) != 0
368
389
val includeLinkedClass = (includes & Include .linkedClass) != 0
369
390
val predicate : NameTree => Boolean = tree =>
@@ -377,7 +398,7 @@ object Interactive {
377
398
)
378
399
)
379
400
)
380
- namedTrees(trees, includeReferences , predicate)
401
+ namedTrees(trees, includes , predicate)
381
402
}
382
403
383
404
/** The reverse path to the node that closest encloses position `pos`,
@@ -465,10 +486,8 @@ object Interactive {
465
486
* @param driver The driver responsible for `path`.
466
487
* @return The definitions for the symbol at the end of `path`.
467
488
*/
468
- def findDefinitions (path : List [Tree ], driver : InteractiveDriver )(implicit ctx : Context ): List [SourceTree ] = {
469
- val sym = enclosingSourceSymbol(path)
470
- if (sym == NoSymbol ) Nil
471
- else {
489
+ def findDefinitions (path : List [Tree ], pos : SourcePosition , driver : InteractiveDriver )(implicit ctx : Context ): List [SourceNamedTree ] = {
490
+ enclosingSourceSymbols(path, pos).flatMap { sym =>
472
491
val enclTree = enclosingTree(path)
473
492
474
493
val (trees, include) =
@@ -492,4 +511,44 @@ object Interactive {
492
511
}
493
512
}
494
513
514
+ /**
515
+ * All the symbols that are imported by import statement `imp`, if it matches
516
+ * the predicate `selectorPredicate`.
517
+ *
518
+ * @param imp The import statement to analyze
519
+ * @param selectorPredicate A test to find the selector to use.
520
+ * @return The symbols imported.
521
+ */
522
+ private def importedSymbols (imp : tpd.Import ,
523
+ selectorPredicate : untpd.Tree => Boolean = util.common.alwaysTrue)
524
+ (implicit ctx : Context ): List [Symbol ] = {
525
+ def lookup0 (name : Name ): Symbol = imp.expr.tpe.member(name).symbol
526
+ def lookup (name : Name ): List [Symbol ] = {
527
+ lookup0(name.toTermName) ::
528
+ lookup0(name.toTypeName) ::
529
+ lookup0(name.moduleClassName) ::
530
+ lookup0(name.sourceModuleName) :: Nil
531
+ }
532
+
533
+ val symbols = imp.selectors.find(selectorPredicate) match {
534
+ case Some (id : untpd.Ident ) =>
535
+ lookup(id.name)
536
+ case Some (Thicket ((id : untpd.Ident ) :: (_ : untpd.Ident ) :: Nil )) =>
537
+ lookup(id.name)
538
+ case _ => Nil
539
+ }
540
+
541
+ symbols.map(sourceSymbol).filter(_.exists).distinct
542
+ }
543
+
544
+ /**
545
+ * Used to represent a renaming import `{foo => bar}`.
546
+ * We need this because the name of the tree must be the new name, but the
547
+ * denotation must be that of the importee.
548
+ */
549
+ private case class RenameTree (name : Name , underlying : Tree ) extends NameTree {
550
+ override def denot (implicit ctx : Context ) = underlying.denot
551
+ myTpe = NoType
552
+ }
553
+
495
554
}
0 commit comments