Skip to content

Commit 5c4d7d2

Browse files
authored
Merge pull request #3960 from dotty-staging/ide-completions
Context-sensitive IDE completions
2 parents e2f72cd + 4d0b9ed commit 5c4d7d2

28 files changed

+681
-334
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,8 +896,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
896896

897897
def decls: List[Symbol] = tp.decls.toList
898898

899-
def members: List[Symbol] =
900-
tp.memberDenots(takeAllFilter, (name, buf) => buf ++= tp.member(name).alternatives).map(_.symbol).toList
899+
def members: List[Symbol] = tp.allMembers.map(_.symbol).toList
901900

902901
def typeSymbol: Symbol = tp.widenDealias.typeSymbol
903902

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object CompilationUnit {
3535
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit =
3636
mkCompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()), unpickled, forceTrees)
3737

38-
/** Make a compilation unit the given unpickled tree */
38+
/** Make a compilation unit, given picked bytes and unpickled tree */
3939
def mkCompilationUnit(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
4040
assert(!unpickled.isEmpty, unpickled)
4141
val unit1 = new CompilationUnit(source)

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
6161
(start.setRun(this) /: defn.RootImportFns)(addImport)
6262
}
6363

64+
private[this] var compiling = false
65+
6466
private[this] var myCtx = rootContext(ictx)
6567

6668
/** The context created for this run */
@@ -72,8 +74,6 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
7274
private[this] var myUnits: List[CompilationUnit] = _
7375
private[this] var myUnitsCached: List[CompilationUnit] = _
7476
private[this] var myFiles: Set[AbstractFile] = _
75-
private[this] val myLateUnits = mutable.ListBuffer[CompilationUnit]()
76-
private[this] var myLateFiles = mutable.Set[AbstractFile]()
7777

7878
/** The compilation units currently being compiled, this may return different
7979
* results over time.
@@ -95,11 +95,11 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
9595
myFiles
9696
}
9797

98-
/** Units that are added from source completers but that are not compiled in current run. */
99-
def lateUnits: List[CompilationUnit] = myLateUnits.toList
98+
/** The source files of all late entered symbols, as a set */
99+
private[this] var lateFiles = mutable.Set[AbstractFile]()
100100

101-
/** The source files of all late units, as a set */
102-
def lateFiles: collection.Set[AbstractFile] = myLateFiles
101+
/** Actions that need to be performed at the end of the current compilation run */
102+
private[this] var finalizeActions = mutable.ListBuffer[() => Unit]()
103103

104104
def getSource(fileName: String): SourceFile = {
105105
val f = new PlainFile(io.Path(fileName))
@@ -148,6 +148,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
148148

149149
protected def compileUnits()(implicit ctx: Context) = Stats.maybeMonitored {
150150
ctx.checkSingleThreaded()
151+
compiling = true
151152

152153
// If testing pickler, make sure to stop after pickling phase:
153154
val stopAfter =
@@ -189,24 +190,31 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
189190
ctx.phases.foreach(_.initContext(runCtx))
190191
runPhases(runCtx)
191192
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
193+
while (finalizeActions.nonEmpty) {
194+
val action = finalizeActions.remove(0)
195+
action()
196+
}
197+
compiling = false
192198
}
193199

194200
/** Enter top-level definitions of classes and objects contain in Scala source file `file`.
195201
* The newly added symbols replace any previously entered symbols.
202+
* If `typeCheck = true`, also run typer on the compilation unit.
196203
*/
197-
def enterRoots(file: AbstractFile)(implicit ctx: Context): Unit =
204+
def lateCompile(file: AbstractFile, typeCheck: Boolean)(implicit ctx: Context): Unit =
198205
if (!files.contains(file) && !lateFiles.contains(file)) {
206+
lateFiles += file
199207
val unit = new CompilationUnit(getSource(file.path))
200-
myLateUnits += unit
201-
myLateFiles += file
202-
enterRoots(unit)(runContext.fresh.setCompilationUnit(unit))
208+
def process()(implicit ctx: Context) = {
209+
unit.untpdTree = new Parser(unit.source).parse()
210+
ctx.typer.lateEnter(unit.untpdTree)
211+
def typeCheckUnit() = unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree)
212+
if (typeCheck)
213+
if (compiling) finalizeActions += (() => typeCheckUnit()) else typeCheckUnit()
214+
}
215+
process()(runContext.fresh.setCompilationUnit(unit))
203216
}
204217

205-
private def enterRoots(unit: CompilationUnit)(implicit ctx: Context): Unit = {
206-
unit.untpdTree = new Parser(unit.source).parse()
207-
ctx.typer.lateEnter(unit.untpdTree)
208-
}
209-
210218
private sealed trait PrintedTree
211219
private /*final*/ case class SomePrintedTree(phase: String, tree: String) extends PrintedTree
212220
private object NoPrintedTree extends PrintedTree

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
873873
def tpes: List[Type] = xs map (_.tpe)
874874
}
875875

876+
/** A trait for loaders that compute trees. Currently implemented just by DottyUnpickler. */
877+
trait TreeProvider {
878+
protected def computeTrees(implicit ctx: Context): List[Tree]
879+
880+
private[this] var myTrees: List[Tree] = null
881+
882+
/** Get trees defined by this provider. Cache them if -Yretain-trees is set. */
883+
def trees(implicit ctx: Context): List[Tree] =
884+
if (ctx.settings.YretainTrees.value) {
885+
if (myTrees == null) myTrees = computeTrees
886+
myTrees
887+
} else computeTrees
888+
889+
/** Get first tree defined by this provider, or EmptyTree if none exists */
890+
def tree(implicit ctx: Context): Tree =
891+
trees.headOption.getOrElse(EmptyTree)
892+
893+
/** Is it possible that the tree to load contains a definition of or reference to `id`? */
894+
def mightContain(id: String)(implicit ctx: Context) = true
895+
}
896+
876897
// convert a numeric with a toXXX method
877898
def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = {
878899
val mname = ("to" + numericCls.name).toTermName

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ object Printers {
1919
val cyclicErrors: Printer = noPrinter
2020
val dottydoc: Printer = noPrinter
2121
val exhaustivity: Printer = noPrinter
22-
val incremental: Printer = noPrinter
2322
val gadts: Printer = noPrinter
2423
val hk: Printer = noPrinter
2524
val implicits: Printer = noPrinter
2625
val implicitsDetailed: Printer = noPrinter
2726
val inlining: Printer = noPrinter
27+
val interactiv: Printer = noPrinter
2828
val overload: Printer = noPrinter
2929
val patmatch: Printer = noPrinter
3030
val pickling: Printer = noPrinter

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,29 @@ object Annotations {
1010

1111
abstract class Annotation {
1212
def tree(implicit ctx: Context): Tree
13+
1314
def symbol(implicit ctx: Context): Symbol =
1415
if (tree.symbol.isConstructor) tree.symbol.owner
1516
else tree.tpe.typeSymbol
17+
1618
def matches(cls: Symbol)(implicit ctx: Context): Boolean = symbol.derivesFrom(cls)
19+
1720
def appliesToModule: Boolean = true // for now; see remark in SymDenotations
1821

1922
def derivedAnnotation(tree: Tree)(implicit ctx: Context) =
2023
if (tree eq this.tree) this else Annotation(tree)
2124

2225
def arguments(implicit ctx: Context) = ast.tpd.arguments(tree)
26+
2327
def argument(i: Int)(implicit ctx: Context): Option[Tree] = {
2428
val args = arguments
2529
if (i < args.length) Some(args(i)) else None
2630
}
2731
def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] =
2832
for (ConstantType(c) <- argument(i) map (_.tpe)) yield c
2933

34+
def isEvaluated: Boolean = true
35+
3036
def ensureCompleted(implicit ctx: Context): Unit = tree
3137
}
3238

@@ -43,6 +49,8 @@ object Annotations {
4349
if (myTree == null) myTree = complete(ctx)
4450
myTree
4551
}
52+
53+
override def isEvaluated = myTree != null
4654
}
4755

4856
/** An annotation indicating the body of a right-hand side,
@@ -73,7 +81,7 @@ object Annotations {
7381
}
7482
myBody
7583
}
76-
def isEvaluated = evaluated
84+
override def isEvaluated = evaluated
7785
}
7886

7987
object Annotation {

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ object Contexts {
131131
private[this] var _typeAssigner: TypeAssigner = _
132132
protected def typeAssigner_=(typeAssigner: TypeAssigner) = _typeAssigner = typeAssigner
133133
def typeAssigner: TypeAssigner = _typeAssigner
134-
def typer: Typer = _typeAssigner.asInstanceOf[Typer]
135134

136135
/** The currently active import info */
137136
private[this] var _importInfo: ImportInfo = _

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,5 +201,9 @@ object Decorators {
201201
def hl(args: Any*)(implicit ctx: Context): String =
202202
new SyntaxFormatter(sc).assemble(args).stripMargin
203203
}
204+
205+
implicit class ArrayInterpolator[T <: AnyRef](val arr: Array[T]) extends AnyVal {
206+
def binarySearch(x: T): Int = java.util.Arrays.binarySearch(arr.asInstanceOf[Array[Object]], x)
207+
}
204208
}
205209

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -789,10 +789,7 @@ object Denotations {
789789
this match {
790790
case symd: SymDenotation =>
791791
if (ctx.stillValid(symd)) return updateValidity()
792-
if (ctx.acceptStale(symd)) {
793-
val newd = symd.owner.info.decls.lookup(symd.name)
794-
return (newd.denot: SingleDenotation).orElse(symd).updateValidity()
795-
}
792+
if (ctx.acceptStale(symd)) return symd.currentSymbol.denot.orElse(symd).updateValidity()
796793
case _ =>
797794
}
798795
if (!symbol.exists) return updateValidity()

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import util.SimpleIdentityMap
1818
import util.Stats
1919
import java.util.WeakHashMap
2020
import config.Config
21-
import config.Printers.{incremental, noPrinter}
21+
import config.Printers.noPrinter
2222
import reporting.diagnostic.Message
2323
import reporting.diagnostic.messages.BadSymbolicReference
2424
import reporting.trace
@@ -206,7 +206,7 @@ object SymDenotations {
206206
* Uncompleted denotations set myInfo to a LazyType.
207207
*/
208208
final def info(implicit ctx: Context): Type = {
209-
def completeInfo = {
209+
def completeInfo = { // Written this way so that `info` is small enough to be inlined
210210
completeFrom(myInfo.asInstanceOf[LazyType]); info
211211
}
212212
if (myInfo.isInstanceOf[LazyType]) completeInfo else myInfo
@@ -959,7 +959,6 @@ object SymDenotations {
959959
}
960960
}
961961

962-
963962
/** The class with the same (type-) name as this module or module class,
964963
* and which is also defined in the same scope and compilation unit.
965964
* NoSymbol if this class does not exist.
@@ -1135,6 +1134,22 @@ object SymDenotations {
11351134
/** The primary constructor of a class or trait, NoSymbol if not applicable. */
11361135
def primaryConstructor(implicit ctx: Context): Symbol = NoSymbol
11371136

1137+
/** The current declaration in this symbol's class owner that has the same name
1138+
* as this one, and, if there are several, also has the same signature.
1139+
*/
1140+
def currentSymbol(implicit ctx: Context): Symbol = {
1141+
val candidates = owner.info.decls.lookupAll(name)
1142+
def test(sym: Symbol): Symbol =
1143+
if (sym == symbol || sym.signature == signature) sym
1144+
else if (candidates.hasNext) test(candidates.next)
1145+
else NoSymbol
1146+
if (candidates.hasNext) {
1147+
val sym = candidates.next
1148+
if (candidates.hasNext) test(sym) else sym
1149+
}
1150+
else NoSymbol
1151+
}
1152+
11381153
// ----- type-related ------------------------------------------------
11391154

11401155
/** The type parameters of a class symbol, Nil for all other symbols */

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import util.Stats
1414
import Decorators._
1515
import scala.util.control.NonFatal
1616
import ast.Trees._
17+
import ast.tpd
1718
import parsing.Parsers.OutlineParser
1819
import reporting.trace
1920

@@ -118,7 +119,9 @@ class SymbolLoaders {
118119
scope: Scope = EmptyScope)(implicit ctx: Context): Unit = {
119120

120121
val completer = new SourcefileLoader(src)
121-
if (ctx.settings.scansource.value) {
122+
if (ctx.settings.scansource.value && ctx.run != null) {
123+
System.out.print(i"scanning $src ...")
124+
System.out.flush()
122125
if (src.exists && !src.isDirectory) {
123126
val filePath = owner.ownersIterator.takeWhile(!_.isRoot).map(_.name.toTermName).toList
124127

@@ -160,6 +163,7 @@ class SymbolLoaders {
160163

161164
val unit = new CompilationUnit(ctx.run.getSource(src.path))
162165
enterScanned(unit)(ctx.run.runContext.fresh.setCompilationUnit(unit))
166+
System.out.println(" done")
163167
}
164168
}
165169
else enterClassAndModule(owner, name, completer, scope = scope)
@@ -338,15 +342,8 @@ abstract class SymbolLoader extends LazyType {
338342
postProcess(root.scalacLinkedClass.denot)
339343
}
340344
}
341-
}
342-
343-
class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
344-
345-
override def sourceFileOrNull: AbstractFile = classfile
346-
347-
def description(implicit ctx: Context) = "class file " + classfile.toString
348345

349-
def rootDenots(rootDenot: ClassDenotation)(implicit ctx: Context): (ClassDenotation, ClassDenotation) = {
346+
protected def rootDenots(rootDenot: ClassDenotation)(implicit ctx: Context): (ClassDenotation, ClassDenotation) = {
350347
val linkedDenot = rootDenot.scalacLinkedClass.denot match {
351348
case d: ClassDenotation => d
352349
case d =>
@@ -368,6 +365,13 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
368365
if (rootDenot is ModuleClass) (linkedDenot, rootDenot)
369366
else (rootDenot, linkedDenot)
370367
}
368+
}
369+
370+
class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
371+
372+
override def sourceFileOrNull: AbstractFile = classfile
373+
374+
def description(implicit ctx: Context) = "class file " + classfile.toString
371375

372376
override def doComplete(root: SymDenotation)(implicit ctx: Context): Unit =
373377
load(root)
@@ -379,8 +383,8 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
379383
if (mayLoadTreesFromTasty) {
380384
result match {
381385
case Some(unpickler: tasty.DottyUnpickler) =>
382-
classRoot.symbol.asClass.unpickler = unpickler
383-
moduleRoot.symbol.asClass.unpickler = unpickler
386+
classRoot.classSymbol.treeOrProvider = unpickler
387+
moduleRoot.classSymbol.treeOrProvider = unpickler
384388
case _ =>
385389
}
386390
}
@@ -394,5 +398,5 @@ class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader {
394398
def description(implicit ctx: Context) = "source file " + srcfile.toString
395399
override def sourceFileOrNull = srcfile
396400
def doComplete(root: SymDenotation)(implicit ctx: Context): Unit =
397-
ctx.run.enterRoots(srcfile)
401+
ctx.run.lateCompile(srcfile, typeCheck = ctx.settings.YretainTrees.value)
398402
}

0 commit comments

Comments
 (0)