From 5bf02b6133983ec0007ad34210035c7aeea83160 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 16 Oct 2018 00:08:28 +0200 Subject: [PATCH 01/19] Able to consume tasty from semanticdb's tests --- semanticdb/test/dotty/semanticdb/Tests.scala | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 980a78d949d9..8da6d085a888 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -25,14 +25,24 @@ class Tests { /** TODO: Produce semanticdb from TASTy for this Scala source file. */ def getTastySemanticdb(scalaFile: Path): s.TextDocument = { - ??? + val scalac = getScalacSemanticdb(scalaFile) + val pat = """(.*)\.scala""".r + val classpath = scalaFile.getParent().toString() + val modulename = sourceDirectory.relativize(scalaFile).getParent().getFileName().toString() + val sourcename = + scalaFile.toFile().getName().toString() match { + case pat(name) => name + case _ => "" + } + val _ = ConsumeTasty(classpath, (modulename + "." + sourcename) :: Nil, new SemanticdbConsumer) + return s.TextDocument(text = scalac.text) } /** Fails the test if the s.TextDocument from tasty and semanticdb-scalac are not the same. */ def checkFile(filename: String): Unit = { val path = sourceDirectory.resolve(filename) val scalac = getScalacSemanticdb(path) - val tasty = s.TextDocument(text = scalac.text) // TODO: replace with `getTastySemanticdb(path)` + val tasty = getTastySemanticdb(path) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) assertNoDiff(obtained, expected) From de3b82af4d1974c1e7664cb961df2e07ca56da25 Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 18 Oct 2018 01:00:41 +0200 Subject: [PATCH 02/19] Start generating some symbols for semanticdb. --- .../dotty/semanticdb/SemanticdbConsumer.scala | 101 ++++++++++++++++-- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 2f7b543cde52..0cef87220d99 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -3,18 +3,107 @@ package dotty.semanticdb import scala.tasty.Reflection import scala.tasty.file.TastyConsumer +import scala.tasty.util.TreeTraverser +import dotty.tools.dotc.tastyreflect +import scala.collection.mutable.HashMap + class SemanticdbConsumer extends TastyConsumer { + var stack : List[String] = Nil + val symbolsDefs : HashMap[String, Int] = HashMap() + val symbolsVals : HashMap[String, Int] = HashMap() + + def insertPathDefDef(path: String): String = { + if (symbolsDefs.contains(path)) { + symbolsDefs += (path -> (symbolsDefs(path) + 1)) + "+" + (symbolsDefs(path) - 1) + } else { + symbolsDefs += (path -> 1) + "" + } + } + def insertPathValDef(path: String): String = { + if (symbolsVals.contains(path)) { + symbolsVals += (path -> (symbolsVals(path) + 1)) + "+" + (symbolsVals(path) - 1) + } else { + symbolsVals += (path -> 1) + "" + } + } final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ object Traverser extends TreeTraverser { - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = tree match { - case IsDefinition(tree) => - println(tree.name) - super.traverseTree(tree) - case tree => - super.traverseTree(tree) + def packageDefToOccurence(term: Term): String = { + //println(term, term.pos.start, term.pos.end) + val Term.Ident(id) = term + return stack.head + id + "/" + } + + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { + val previous_path = stack.head + + tree match { + case IsClassDef(body) => + val ClassDef(name, _, _, _, _) = body + //println("[classdef] ", body) + val path = stack.head + name + "#" + println(path) + stack = path :: stack + super.traverseTree(body) + stack = stack.tail + case IsTypeDef(body) => + println("[typedef] ", body) + super.traverseTree(body) + case IsDefDef(body) => + val DefDef(name, _, _, _, _) = body + val def_atom = + name match { + case "" => "``" + case _ => name + } + val path_repr = stack.head + def_atom + val path = path_repr + "(" + insertPathDefDef(path_repr) + ")." + println(path) + //println("[defdef] ", body) + stack = path :: stack + super.traverseTree(body) + stack = stack.tail + case IsValDef(body) => + val ValDef(name, _, _) = body + val path_repr = stack.head + name + val path = path_repr + "(" + insertPathValDef(path_repr) + ")" + println(path) + //println("[defdef] ", body) + stack = path :: stack + super.traverseTree(body) + stack = stack.tail + case IsPackageDef(body) => + println("[packagedef] ", body) + super.traverseTree(body) + case IsDefinition(body) => + println("[definition] ", body) + super.traverseTree(body) + case IsPackageClause(body) => + //println(body.pos.start, body.pos.end) + val PackageClause(name, list_tree : List[Tree]) = body + //println(tree) + val path = packageDefToOccurence(name) + println(path) + stack = path :: stack + // call to traverse tree instead of super.traverseTree to avoid + // skipping this child entirely (super.xx will traverse subtrees) + //list_tree.foreach{traverseTree} + /*case IsTerm(body) => + //println("[term] ", body) + super.traverseTree(body)*/ + // iterating this way will probably make us see terms we don't want + super.traverseTree(body) + stack = stack.tail + case tree => + super.traverseTree(tree) + } } } From 47d6d54ea4980eb73a28848ff79b3221039693a4 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 23 Oct 2018 11:24:33 +0200 Subject: [PATCH 03/19] skeleton code to build symbols while iterating over owners --- .../dotty/semanticdb/SemanticdbConsumer.scala | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 0cef87220d99..8900d8c93581 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -41,11 +41,32 @@ class SemanticdbConsumer extends TastyConsumer { return stack.head + id + "/" } + def iterateParent(symbol: Symbol): String = { + if (symbol.name == "") then { + // TODO had a "NoDenotation" test to avoid + // relying on the name itself + "" + } else { + val previous_symbol = iterateParent(symbol.owner) + val next_atom = + symbol match { + case IsPackageSymbol(symbol) => symbol.name + "/" + case IsClassSymbol(symbol) => symbol.name + "#" + case IsDefSymbol(symbol) => symbol.name + "." + case IsValSymbol(symbol) => symbol.name + "." + case owner => { + "" + } + } + previous_symbol + next_atom + } + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { val previous_path = stack.head tree match { - case IsClassDef(body) => + /*case IsClassDef(body) => val ClassDef(name, _, _, _, _) = body //println("[classdef] ", body) val path = stack.head + name + "#" @@ -81,11 +102,12 @@ class SemanticdbConsumer extends TastyConsumer { stack = stack.tail case IsPackageDef(body) => println("[packagedef] ", body) - super.traverseTree(body) + super.traverseTree(body)*/ case IsDefinition(body) => - println("[definition] ", body) + //println("[definition] ", body) + println(iterateParent(tree.symbol)) super.traverseTree(body) - case IsPackageClause(body) => + /*case IsPackageClause(body) => //println(body.pos.start, body.pos.end) val PackageClause(name, list_tree : List[Tree]) = body //println(tree) @@ -100,7 +122,7 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(body)*/ // iterating this way will probably make us see terms we don't want super.traverseTree(body) - stack = stack.tail + stack = stack.tail*/ case tree => super.traverseTree(tree) } From d8899759d10464e6a2fc461ace0eed1061f7c28e Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 23 Oct 2018 15:52:11 +0200 Subject: [PATCH 04/19] basic disimbiguator --- .../dotty/semanticdb/SemanticdbConsumer.scala | 61 +++++++++++++------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 8900d8c93581..c39c1a22283b 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -8,7 +8,9 @@ import dotty.tools.dotc.tastyreflect import scala.collection.mutable.HashMap class SemanticdbConsumer extends TastyConsumer { - var stack : List[String] = Nil + var stack: List[String] = Nil + + /* val symbolsDefs : HashMap[String, Int] = HashMap() val symbolsVals : HashMap[String, Int] = HashMap() @@ -29,11 +31,13 @@ class SemanticdbConsumer extends TastyConsumer { symbolsVals += (path -> 1) "" } - } + }*/ final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ object Traverser extends TreeTraverser { + val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() + val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() def packageDefToOccurence(term: Term): String = { //println(term, term.pos.start, term.pos.end) @@ -41,29 +45,48 @@ class SemanticdbConsumer extends TastyConsumer { return stack.head + id + "/" } + def disimbiguate(symbol_path: String): String = { + if (symbolPathsDisimbiguator.contains(symbol_path)) { + symbolPathsDisimbiguator += + (symbol_path -> (symbolPathsDisimbiguator(symbol_path) + 1)) + "(+" + (symbolPathsDisimbiguator(symbol_path) - 1) + ")" + } else { + symbolPathsDisimbiguator += (symbol_path -> 1) + "()" + } + } + def iterateParent(symbol: Symbol): String = { - if (symbol.name == "") then { - // TODO had a "NoDenotation" test to avoid - // relying on the name itself - "" + if (symbolsCache.contains(symbol)) { + return symbolsCache(symbol) } else { - val previous_symbol = iterateParent(symbol.owner) - val next_atom = - symbol match { - case IsPackageSymbol(symbol) => symbol.name + "/" - case IsClassSymbol(symbol) => symbol.name + "#" - case IsDefSymbol(symbol) => symbol.name + "." - case IsValSymbol(symbol) => symbol.name + "." - case owner => { - "" - } - } - previous_symbol + next_atom + val out_symbol_path = + if (symbol.name == "") then { + // TODO had a "NoDenotation" test to avoid + // relying on the name itself + "" + } else { + val previous_symbol = iterateParent(symbol.owner) + val next_atom = + symbol match { + case IsPackageSymbol(symbol) => symbol.name + "/" + case IsClassSymbol(symbol) => symbol.name + "#" + case IsDefSymbol(symbol) => + symbol.name + disimbiguate(previous_symbol + symbol.name) + "." + case IsValSymbol(symbol) => symbol.name + "." + case owner => { + "" + } + } + previous_symbol + next_atom + } + symbolsCache += (symbol -> out_symbol_path) + out_symbol_path } } override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - val previous_path = stack.head + val previous_path = stack.head tree match { /*case IsClassDef(body) => From df894634520e92ec233bb7a4b125f35e54c58a92 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 23 Oct 2018 23:43:09 +0200 Subject: [PATCH 05/19] add namePos to tastyreflect symbol and start generating valid semanticdb --- .../dotc/tastyreflect/PositionOpsImpl.scala | 1 - .../dotc/tastyreflect/SymbolOpsImpl.scala | 2 + .../src/scala/tasty/reflect/SymbolOps.scala | 3 +- project/Build.scala | 3 + .../dotty/semanticdb/SemanticdbConsumer.scala | 86 +++++++------------ semanticdb/test/dotty/semanticdb/Tests.scala | 6 +- 6 files changed, 43 insertions(+), 58 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala index 5fce3371dfa4..f318143233fd 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala @@ -14,5 +14,4 @@ trait PositionOpsImpl extends scala.tasty.reflect.PositionOps with CoreImpl { def startColumn: Int = pos.startColumn def endColumn: Int = pos.endColumn } - } diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index b0be74120ce9..155dd16acbd8 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -26,6 +26,8 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def name(implicit ctx: Context): String = symbol.name.toString def fullName(implicit ctx: Context): String = symbol.fullName.toString + def pos(implicit ctx: Context): Position = symbol.pos + def owner(implicit ctx: Context): Symbol = symbol.owner def localContext(implicit ctx: Context): Context = { diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index b49a8b550017..33a006be1020 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -26,7 +26,8 @@ trait SymbolOps extends Core { /** The full name of this symbol up to the root package. */ def fullName(implicit ctx: Context): String - /** Returns the context within this symbol. */ + def pos(implicit ctx: Context): Position + def localContext(implicit ctx: Context): Context /** Unsafe cast as to PackageSymbol. Use IsPackageSymbol to safly check and cast to PackageSymbol */ diff --git a/project/Build.scala b/project/Build.scala index de79d4d8a79b..dda58196c033 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -393,6 +393,9 @@ object Build { baseDirectory in Test := baseDirectory.value / "..", unmanagedSourceDirectories in Test += baseDirectory.value / "input" / "src" / "main" / "scala", libraryDependencies ++= List( + ("org.scalameta" %% "semanticdb" % "4.0.0" % Compile).withDottyCompat(scalaVersion.value), + "com.novocode" % "junit-interface" % "0.11" % Compile, + "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % Compile, ("org.scalameta" %% "semanticdb" % "4.0.0" % Test).withDottyCompat(scalaVersion.value), "com.novocode" % "junit-interface" % "0.11" % Test, "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % Test diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index c39c1a22283b..c05c21df1af9 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -6,6 +6,7 @@ import scala.tasty.file.TastyConsumer import scala.tasty.util.TreeTraverser import dotty.tools.dotc.tastyreflect import scala.collection.mutable.HashMap +import scala.meta.internal.{semanticdb => s} class SemanticdbConsumer extends TastyConsumer { var stack: List[String] = Nil @@ -32,6 +33,12 @@ class SemanticdbConsumer extends TastyConsumer { "" } }*/ + val semantic: s.TextDocument = s.TextDocument() + var occurrences: Seq[s.SymbolOccurrence] = Seq() + + def toSemanticdb(text: String): s.TextDocument = { + s.TextDocument(text = text, occurrences = occurrences) + } final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -65,6 +72,10 @@ class SemanticdbConsumer extends TastyConsumer { // TODO had a "NoDenotation" test to avoid // relying on the name itself "" + } else if (symbol.name == "") then { + // TODO had a "NoDenotation" test to avoid + // relying on the name itself + "" } else { val previous_symbol = iterateParent(symbol.owner) val next_atom = @@ -89,63 +100,30 @@ class SemanticdbConsumer extends TastyConsumer { val previous_path = stack.head tree match { - /*case IsClassDef(body) => - val ClassDef(name, _, _, _, _) = body - //println("[classdef] ", body) - val path = stack.head + name + "#" - println(path) - stack = path :: stack - super.traverseTree(body) - stack = stack.tail - case IsTypeDef(body) => - println("[typedef] ", body) - super.traverseTree(body) - case IsDefDef(body) => - val DefDef(name, _, _, _, _) = body - val def_atom = - name match { - case "" => "``" - case _ => name - } - val path_repr = stack.head + def_atom - val path = path_repr + "(" + insertPathDefDef(path_repr) + ")." - println(path) - //println("[defdef] ", body) - stack = path :: stack - super.traverseTree(body) - stack = stack.tail - case IsValDef(body) => - val ValDef(name, _, _) = body - val path_repr = stack.head + name - val path = path_repr + "(" + insertPathValDef(path_repr) + ")" - println(path) - //println("[defdef] ", body) - stack = path :: stack - super.traverseTree(body) - stack = stack.tail - case IsPackageDef(body) => - println("[packagedef] ", body) - super.traverseTree(body)*/ case IsDefinition(body) => //println("[definition] ", body) - println(iterateParent(tree.symbol)) - super.traverseTree(body) - /*case IsPackageClause(body) => - //println(body.pos.start, body.pos.end) - val PackageClause(name, list_tree : List[Tree]) = body - //println(tree) - val path = packageDefToOccurence(name) - println(path) - stack = path :: stack - // call to traverse tree instead of super.traverseTree to avoid - // skipping this child entirely (super.xx will traverse subtrees) - //list_tree.foreach{traverseTree} - /*case IsTerm(body) => - //println("[term] ", body) - super.traverseTree(body)*/ - // iterating this way will probably make us see terms we don't want + val symbol_path = iterateParent(tree.symbol) + + val range = + if (tree.symbol.name == "") { + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn, + tree.symbol.pos.startLine, + tree.symbol.pos.endColumn) + } else { + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn, + tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + tree.symbol.name.length) + } + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + symbol_path, + s.SymbolOccurrence.Role.DEFINITION + ) super.traverseTree(body) - stack = stack.tail*/ case tree => super.traverseTree(tree) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 8da6d085a888..469ec765ebfe 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -34,8 +34,10 @@ class Tests { case pat(name) => name case _ => "" } - val _ = ConsumeTasty(classpath, (modulename + "." + sourcename) :: Nil, new SemanticdbConsumer) - return s.TextDocument(text = scalac.text) + val sdbconsumer = new SemanticdbConsumer + val _ = ConsumeTasty(classpath, (modulename + "." + sourcename) :: Nil, sdbconsumer) + sdbconsumer.toSemanticdb(scalac.text) + } /** Fails the test if the s.TextDocument from tasty and semanticdb-scalac are not the same. */ From 11828f27e1459b07f22febdc2f82e3d14df04b23 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 28 Oct 2018 22:27:49 +0100 Subject: [PATCH 06/19] symbol generations for terms and types --- .../dotc/tastyreflect/PositionOpsImpl.scala | 2 + .../TypeOrBoundsTreesOpsImpl.scala | 2 + .../src/scala/tasty/reflect/PositionOps.scala | 3 +- .../tasty/reflect/TypeOrBoundsTreeOps.scala | 2 + .../dotty/semanticdb/SemanticdbConsumer.scala | 122 +++++++++++++----- 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala index f318143233fd..66d3bcac08f9 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/PositionOpsImpl.scala @@ -6,6 +6,8 @@ trait PositionOpsImpl extends scala.tasty.reflect.PositionOps with CoreImpl { def start: Int = pos.start def end: Int = pos.end + def exists: Boolean = pos.exists + def sourceFile: java.nio.file.Path = pos.source.file.jpath def startLine: Int = pos.startLine diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala index 5532f327ae92..f918b02f4c4b 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala @@ -31,6 +31,8 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w object TypeTree extends TypeTreeModule { + def symbol(x: TypeTree)(implicit ctx: Context): Symbol = + x.symbol object Synthetic extends SyntheticExtractor { def unapply(x: TypeTree)(implicit ctx: Context): Boolean = x match { case x @ Trees.TypeTree() => !x.tpe.isInstanceOf[Types.TypeBounds] diff --git a/library/src/scala/tasty/reflect/PositionOps.scala b/library/src/scala/tasty/reflect/PositionOps.scala index 7b5d677f109d..aba9fd344a02 100644 --- a/library/src/scala/tasty/reflect/PositionOps.scala +++ b/library/src/scala/tasty/reflect/PositionOps.scala @@ -4,7 +4,8 @@ trait PositionOps extends Core { trait PositionAPI { - /** The path of source file */ + def exists: Boolean + def sourceFile: java.nio.file.Path /** The start index in the source file */ diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala index e783548c5526..8598358154fa 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala @@ -28,6 +28,8 @@ trait TypeOrBoundsTreeOps extends Core { val TypeTree: TypeTreeModule abstract class TypeTreeModule { + def symbol(x: TypeTree)(implicit ctx: Context): Symbol + /** TypeTree containing an inferred type */ val Synthetic: SyntheticExtractor abstract class SyntheticExtractor { diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index c05c21df1af9..94cd56ce2618 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -79,14 +79,17 @@ class SemanticdbConsumer extends TastyConsumer { } else { val previous_symbol = iterateParent(symbol.owner) val next_atom = - symbol match { - case IsPackageSymbol(symbol) => symbol.name + "/" - case IsClassSymbol(symbol) => symbol.name + "#" - case IsDefSymbol(symbol) => - symbol.name + disimbiguate(previous_symbol + symbol.name) + "." - case IsValSymbol(symbol) => symbol.name + "." - case owner => { - "" + if (symbol.flags.isParam) "(" + symbol.name + ")" + else { + symbol match { + case IsPackageSymbol(symbol) => symbol.name + "/" + case IsClassSymbol(symbol) => symbol.name + "#" + case IsDefSymbol(symbol) => + symbol.name + disimbiguate(previous_symbol + symbol.name) + "." + case IsValSymbol(symbol) => symbol.name + "." + case owner => { + "" + } } } previous_symbol + next_atom @@ -96,40 +99,95 @@ class SemanticdbConsumer extends TastyConsumer { } } - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - val previous_path = stack.head + def addOccurence(symbol: Symbol, + type_symbol: s.SymbolOccurrence.Role, + range: s.Range): Unit = { + //if (symbolsCache.contains(symbol)) return + + val symbol_path = iterateParent(symbol) + if (symbol_path == "" || symbol.name == "") return + + //println(symbol_path, symbol, range) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + symbol_path, + type_symbol + ) + } + + def range(pos: Position, name: String): s.Range = { + val range_end_column = + if (name == "") { + pos.endColumn + } else { + pos.startColumn + name.length + } + + s.Range(pos.startLine, pos.startColumn, pos.startLine, range_end_column) + } + + def rangeExclude(range: Position, exclude: Position): s.Range = { + def max(a: Int, b: Int): Int = { if (a > b) a else b } + return s.Range(max(range.startLine, exclude.startLine), + max(range.startColumn, exclude.startColumn) + 1, + range.endLine, + range.endColumn) + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { + //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) tree match { - case IsDefinition(body) => - //println("[definition] ", body) - val symbol_path = iterateParent(tree.symbol) - - val range = - if (tree.symbol.name == "") { - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn, - tree.symbol.pos.startLine, - tree.symbol.pos.endColumn) - } else { - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn, - tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + tree.symbol.name.length) + case IsDefinition(body) => { + + def typetreeSymbol(typetree: TypeTree): Unit = + typetree match { + case TypeTree.Synthetic => () + case _ => + println(tree.symbol, + typetree, + iterateParent(TypeTree.symbol(typetree))) + addOccurence( + TypeTree.symbol(typetree), + s.SymbolOccurrence.Role.REFERENCE, + range(typetree.pos, TypeTree.symbol(typetree).name)) } - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - symbol_path, - s.SymbolOccurrence.Role.DEFINITION - ) + + val _ = tree match { + case DefDef(_, _, _, typetree, _) => typetreeSymbol(typetree) + case ValDef(_, typetree, _) => typetreeSymbol(typetree) + case _ => () + } + + addOccurence(tree.symbol, + s.SymbolOccurrence.Role.DEFINITION, + range(tree.symbol.pos, tree.symbol.name)) + super.traverseTree(body) + } + + case Term.Select(qualifier, _, _) => { + val range = rangeExclude(tree.pos, qualifier.pos) + addOccurence(tree.symbol, s.SymbolOccurrence.Role.REFERENCE, range) + super.traverseTree(tree) + } + + case Term.Ident(body) => { + //println(tree.pos.startColumn, tree.pos.endColumn) + //println(tree.namePos.startColumn, tree.namePos.endColumn) + addOccurence(tree.symbol, + s.SymbolOccurrence.Role.REFERENCE, + range(tree.pos, tree.symbol.name)) + super.traverseTree(tree) + } case tree => super.traverseTree(tree) } } } + Traverser.traverseTree(root)(reflect.rootContext) } From a320d6e51159864317e14ed8f34cae02c90e3290 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 31 Oct 2018 12:40:46 +0100 Subject: [PATCH 07/19] add makefile to regenerate test files --- semanticdb/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 semanticdb/Makefile diff --git a/semanticdb/Makefile b/semanticdb/Makefile new file mode 100644 index 000000000000..dcf105eecb4e --- /dev/null +++ b/semanticdb/Makefile @@ -0,0 +1,11 @@ +path="input/src/main/scala/" +files=$(shell find $(path) -type f -name '*.scala') + +all: %.tasty + cd input; sbt compile + +test: all + cd ..; sbt dotty-semanticdb/test; cd - + +%.tasty: $(files) + cd $(dir $^); $(CURDIR)/../bin/dotc $(notdir $^) From 79e07e7b9f3f1d70ef3d063f633f34a8ab84a64d Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 31 Oct 2018 15:57:50 +0100 Subject: [PATCH 08/19] small update for types --- .../TypeOrBoundsTreesOpsImpl.scala | 3 +- .../tasty/reflect/TypeOrBoundsTreeOps.scala | 1 - .../dotty/semanticdb/SemanticdbConsumer.scala | 46 ++++++++++--------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala index f918b02f4c4b..673c1a640d3f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala @@ -18,6 +18,7 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI = new TypeTreeAPI { def pos(implicit ctx: Context): Position = tpt.pos + def symbol(implicit ctx: Context): Symbol = tpt.symbol def tpe(implicit ctx: Context): Type = tpt.tpe.stripTypeVar def symbol(implicit ctx: Context): Symbol = tpt.symbol } @@ -31,8 +32,6 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w object TypeTree extends TypeTreeModule { - def symbol(x: TypeTree)(implicit ctx: Context): Symbol = - x.symbol object Synthetic extends SyntheticExtractor { def unapply(x: TypeTree)(implicit ctx: Context): Boolean = x match { case x @ Trees.TypeTree() => !x.tpe.isInstanceOf[Types.TypeBounds] diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala index 8598358154fa..c7f02b2678e8 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala @@ -28,7 +28,6 @@ trait TypeOrBoundsTreeOps extends Core { val TypeTree: TypeTreeModule abstract class TypeTreeModule { - def symbol(x: TypeTree)(implicit ctx: Context): Symbol /** TypeTree containing an inferred type */ val Synthetic: SyntheticExtractor diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 94cd56ce2618..996d7438c95c 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -83,10 +83,13 @@ class SemanticdbConsumer extends TastyConsumer { else { symbol match { case IsPackageSymbol(symbol) => symbol.name + "/" - case IsClassSymbol(symbol) => symbol.name + "#" case IsDefSymbol(symbol) => symbol.name + disimbiguate(previous_symbol + symbol.name) + "." case IsValSymbol(symbol) => symbol.name + "." + case IsTypeSymbol(symbol) => + symbol.name + "#" + case IsClassSymbol(symbol) => + symbol.name + (if (symbol.flags.isObject) "." else "#") case owner => { "" } @@ -107,7 +110,6 @@ class SemanticdbConsumer extends TastyConsumer { val symbol_path = iterateParent(symbol) if (symbol_path == "" || symbol.name == "") return - //println(symbol_path, symbol, range) occurrences = occurrences :+ s.SymbolOccurrence( @@ -136,30 +138,26 @@ class SemanticdbConsumer extends TastyConsumer { range.endColumn) } + def typetreeSymbol(typetree: TypeTree): Unit = + typetree match { + case TypeTree.Synthetic => () + case _ => + addOccurence(typetree.symbol, + s.SymbolOccurrence.Role.REFERENCE, + range(typetree.pos, typetree.symbol.name)) + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) tree match { case IsDefinition(body) => { - - def typetreeSymbol(typetree: TypeTree): Unit = - typetree match { - case TypeTree.Synthetic => () - case _ => - println(tree.symbol, - typetree, - iterateParent(TypeTree.symbol(typetree))) - addOccurence( - TypeTree.symbol(typetree), - s.SymbolOccurrence.Role.REFERENCE, - range(typetree.pos, TypeTree.symbol(typetree).name)) + if (tree.symbol.name != "") { + val _ = tree match { + case DefDef(_, _, _, typetree, _) => typetreeSymbol(typetree) + case ValDef(_, typetree, _) => typetreeSymbol(typetree) + case _ => () } - - val _ = tree match { - case DefDef(_, _, _, typetree, _) => typetreeSymbol(typetree) - case ValDef(_, typetree, _) => typetreeSymbol(typetree) - case _ => () } - addOccurence(tree.symbol, s.SymbolOccurrence.Role.DEFINITION, range(tree.symbol.pos, tree.symbol.name)) @@ -173,7 +171,7 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(tree) } - case Term.Ident(body) => { + case Term.Ident(_) => { //println(tree.pos.startColumn, tree.pos.endColumn) //println(tree.namePos.startColumn, tree.namePos.endColumn) addOccurence(tree.symbol, @@ -181,6 +179,12 @@ class SemanticdbConsumer extends TastyConsumer { range(tree.pos, tree.symbol.name)) super.traverseTree(tree) } + case PackageClause(_) => + addOccurence(tree.symbol, + s.SymbolOccurrence.Role.REFERENCE, + range(tree.pos, tree.symbol.name)) + super.traverseTree(tree) + case tree => super.traverseTree(tree) } From 9aa33745b0e4da310897590e4add161e22b53b86 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 6 Nov 2018 18:23:31 +0100 Subject: [PATCH 09/19] import Scala.scala from original implementation of semanticdb --- semanticdb/src/dotty/semanticdb/Scala.scala | 290 ++++++++++++++++++ .../dotty/semanticdb/SemanticdbConsumer.scala | 76 +++-- 2 files changed, 339 insertions(+), 27 deletions(-) create mode 100644 semanticdb/src/dotty/semanticdb/Scala.scala diff --git a/semanticdb/src/dotty/semanticdb/Scala.scala b/semanticdb/src/dotty/semanticdb/Scala.scala new file mode 100644 index 000000000000..b4bd983022a5 --- /dev/null +++ b/semanticdb/src/dotty/semanticdb/Scala.scala @@ -0,0 +1,290 @@ +package dotty.semanticdb + +import scala.compat.Platform.EOL +import dotty.semanticdb.Scala.{Descriptor => d} +import dotty.semanticdb.Scala.{Names => n} + +object Scala { + object Symbols { + val None: String = "" + val RootPackage: String = "_root_/" + val EmptyPackage: String = "_empty_/" + def Global(owner: String, desc: Descriptor): String = + if (owner != RootPackage) owner + desc.toString + else desc.toString + def Local(suffix: String): String = { + if (suffix.indexOf("/") == -1 && suffix.indexOf(";") == -1) "local" + suffix + else throw new IllegalArgumentException(suffix) + } + def Multi(symbols: List[String]): String = { + symbols.distinct match { + case List(symbol) => + symbol + case symbols => + val sb = new StringBuilder + symbols.foreach { symbol => + if (!symbol.isMulti) { + sb.append(';') + } + sb.append(symbol) + } + sb.toString() + } + } + } + + implicit class ScalaSymbolOps(symbol: String) { + def isNone: Boolean = + symbol == Symbols.None + def isRootPackage: Boolean = + symbol == Symbols.RootPackage + def isEmptyPackage: Boolean = + symbol == Symbols.EmptyPackage + def isGlobal: Boolean = + !isNone && !isMulti && (symbol.last match { + case '.' | '#' | '/' | ')' | ']' => true + case _ => false + }) + def isLocal: Boolean = + symbol.startsWith("local") + def isMulti: Boolean = + symbol.startsWith(";") + def asMulti: List[String] = { + if (!isMulti) symbol :: Nil + else { + val buf = List.newBuilder[String] + def loop(begin: Int, i: Int): Unit = + if (i >= symbol.length) { + buf += symbol.substring(begin, symbol.length) + } else { + symbol.charAt(i) match { + case ';' => + buf += symbol.substring(begin, i) + loop(i + 1, i + 1) + case '`' => + var j = i + 1 + while (symbol.charAt(j) != '`') j += 1 + loop(begin, j + 1) + case _ => + loop(begin, i + 1) + } + } + loop(1, 1) + buf.result() + } + } + def isTerm: Boolean = + !isNone && !isMulti && symbol.last == '.' + def isType: Boolean = + !isNone && !isMulti && symbol.last == '#' + def isPackage: Boolean = + !isNone && !isMulti && symbol.last == '/' + def isParameter: Boolean = + !isNone && !isMulti && symbol.last == ')' + def isTypeParameter: Boolean = + !isNone && !isMulti && symbol.last == ']' + def ownerChain: List[String] = { + val buf = List.newBuilder[String] + def loop(symbol: String): Unit = { + if (!symbol.isNone) { + loop(symbol.owner) + buf += symbol + } + } + loop(symbol) + buf.result + } + def owner: String = { + if (isGlobal) { + if (isRootPackage) Symbols.None + else { + val rest = DescriptorParser(symbol)._2 + if (rest.nonEmpty) rest + else Symbols.RootPackage + } + } else { + Symbols.None + } + } + def desc: Descriptor = { + if (isGlobal) { + DescriptorParser(symbol)._1 + } else { + d.None + } + } + } + + sealed trait Descriptor { + def isNone: Boolean = this == d.None + def isTerm: Boolean = this.isInstanceOf[d.Term] + def isMethod: Boolean = this.isInstanceOf[d.Method] + def isType: Boolean = this.isInstanceOf[d.Type] + def isPackage: Boolean = this.isInstanceOf[d.Package] + def isParameter: Boolean = this.isInstanceOf[d.Parameter] + def isTypeParameter: Boolean = this.isInstanceOf[d.TypeParameter] + def value: String + def name: n.Name = { + this match { + case d.None => n.TermName(value) + case d.Term(value) => n.TermName(value) + case d.Method(value, disambiguator) => n.TermName(value) + case d.Type(value) => n.TypeName(value) + case d.Package(value) => n.TermName(value) + case d.Parameter(value) => n.TermName(value) + case d.TypeParameter(value) => n.TypeName(value) + } + } + override def toString: String = { + this match { + case d.None => sys.error("unsupported descriptor") + case d.Term(value) => s"${n.encode(value)}." + case d.Method(value, disambiguator) => s"${n.encode(value)}${disambiguator}." + case d.Type(value) => s"${n.encode(value)}#" + case d.Package(value) => s"${n.encode(value)}/" + case d.Parameter(value) => s"(${n.encode(value)})" + case d.TypeParameter(value) => s"[${n.encode(value)}]" + } + } + } + object Descriptor { + final case object None extends Descriptor { def value: String = "" } + final case class Term(value: String) extends Descriptor + final case class Method(value: String, disambiguator: String) extends Descriptor + final case class Type(value: String) extends Descriptor + final case class Package(value: String) extends Descriptor + final case class Parameter(value: String) extends Descriptor + final case class TypeParameter(value: String) extends Descriptor + } + + object Names { + // NOTE: This trait is defined inside Names to support the idiom of importing + // scala.meta.internal.semanticdb.Scala._ and not being afraid of name conflicts. + sealed trait Name { + def value: String + override def toString: String = value + } + final case class TermName(value: String) extends Name + final case class TypeName(value: String) extends Name + + val RootPackage: TermName = TermName("_root_") + val EmptyPackage: TermName = TermName("_empty_") + val PackageObject: TermName = TermName("package") + val Constructor: TermName = TermName("") + + private[semanticdb] def encode(value: String): String = { + if (value == "") { + "``" + } else { + val (start, parts) = (value.head, value.tail) + val isStartOk = Character.isJavaIdentifierStart(start) + val isPartsOk = parts.forall(Character.isJavaIdentifierPart) + if (isStartOk && isPartsOk) value + else "`" + value + "`" + } + } + } + + object DisplayNames { + val RootPackage: String = "_root_" + val EmptyPackage: String = "_empty_" + val Constructor: String = "" + val Anonymous: String = "_" + } + + private class DescriptorParser(s: String) { + var i = s.length + def fail() = { + val message = "invalid symbol format" + val caret = " " * i + "^" + sys.error(s"$message$EOL$s$EOL$caret") + } + + val BOF = '\u0000' + val EOF = '\u001A' + var currChar = EOF + def readChar(): Char = { + if (i <= 0) { + if (i == 0) { + i -= 1 + currChar = BOF + currChar + } else { + fail() + } + } else { + i -= 1 + currChar = s(i) + currChar + } + } + + def parseValue(): String = { + if (currChar == '`') { + val end = i + while (readChar() != '`') {} + readChar() + s.substring(i + 2, end) + } else { + val end = i + 1 + if (!Character.isJavaIdentifierPart(currChar)) fail() + while (Character.isJavaIdentifierPart(readChar()) && currChar != BOF) {} + s.substring(i + 1, end) + } + } + + def parseDisambiguator(): String = { + val end = i + 1 + if (currChar != ')') fail() + while (readChar() != '(') {} + readChar() + s.substring(i + 1, end) + } + + def parseDescriptor(): Descriptor = { + if (currChar == '.') { + readChar() + if (currChar == ')') { + val disambiguator = parseDisambiguator() + val value = parseValue() + d.Method(value, disambiguator) + } else { + d.Term(parseValue()) + } + } else if (currChar == '#') { + readChar() + d.Type(parseValue()) + } else if (currChar == '/') { + readChar() + d.Package(parseValue()) + } else if (currChar == ')') { + readChar() + val value = parseValue() + if (currChar != '(') fail() + else readChar() + d.Parameter(value) + } else if (currChar == ']') { + readChar() + val value = parseValue() + if (currChar != '[') fail() + else readChar() + d.TypeParameter(value) + } else { + fail() + } + } + + def entryPoint(): (Descriptor, String) = { + readChar() + val desc = parseDescriptor() + (desc, s.substring(0, i + 1)) + } + } + + private[semanticdb] object DescriptorParser { + def apply(symbol: String): (Descriptor, String) = { + val parser = new DescriptorParser(symbol) + parser.entryPoint() + } + } +} diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 996d7438c95c..c4b63d8faf8e 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -7,6 +7,8 @@ import scala.tasty.util.TreeTraverser import dotty.tools.dotc.tastyreflect import scala.collection.mutable.HashMap import scala.meta.internal.{semanticdb => s} +import dotty.semanticdb.Scala.{Descriptor => d} +import dotty.semanticdb.Scala._ class SemanticdbConsumer extends TastyConsumer { var stack: List[String] = Nil @@ -40,18 +42,45 @@ class SemanticdbConsumer extends TastyConsumer { s.TextDocument(text = text, occurrences = occurrences) } + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ object Traverser extends TreeTraverser { val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() - def packageDefToOccurence(term: Term): String = { - //println(term, term.pos.start, term.pos.end) - val Term.Ident(id) = term - return stack.head + id + "/" + implicit class SymbolExtender(symbol: Symbol) { + def isTypeParameter: Boolean = symbol match { + case IsTypeSymbol(_) => symbol.flags.isParam + case _ => false + } + + def isType: Boolean = symbol match { + case IsTypeSymbol(_) => true + case _ => false + } + + def isMethod: Boolean = symbol match { + case IsDefSymbol(_) => true + case _ => false + } + + def isPackage: Boolean = symbol match { + case IsPackageSymbol(_) => true + case _ => false + } + + def isTrait: Boolean = symbol.flags.isTrait + + def isValueParameter: Boolean = symbol.flags.isParam + + // TODO : implement it + def isJavaClass: Boolean = false } + val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() + val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() + def disimbiguate(symbol_path: String): String = { if (symbolPathsDisimbiguator.contains(symbol_path)) { symbolPathsDisimbiguator += @@ -62,40 +91,33 @@ class SemanticdbConsumer extends TastyConsumer { "()" } } - def iterateParent(symbol: Symbol): String = { if (symbolsCache.contains(symbol)) { return symbolsCache(symbol) } else { val out_symbol_path = - if (symbol.name == "") then { - // TODO had a "NoDenotation" test to avoid - // relying on the name itself - "" - } else if (symbol.name == "") then { + if (symbol.name == "" || symbol.name == "") then { // TODO had a "NoDenotation" test to avoid // relying on the name itself "" } else { val previous_symbol = iterateParent(symbol.owner) val next_atom = - if (symbol.flags.isParam) "(" + symbol.name + ")" - else { - symbol match { - case IsPackageSymbol(symbol) => symbol.name + "/" - case IsDefSymbol(symbol) => - symbol.name + disimbiguate(previous_symbol + symbol.name) + "." - case IsValSymbol(symbol) => symbol.name + "." - case IsTypeSymbol(symbol) => - symbol.name + "#" - case IsClassSymbol(symbol) => - symbol.name + (if (symbol.flags.isObject) "." else "#") - case owner => { - "" - } - } - } - previous_symbol + next_atom + if (symbol.isPackage) { + d.Package(symbol.name) + } else if (symbol.isTypeParameter) { + d.TypeParameter(symbol.name) + } else if (symbol.isMethod) { + d.Method(symbol.name, disimbiguate(previous_symbol + symbol.name)) + } else if (symbol.isValueParameter) { + d.Parameter(symbol.name) + } else if (symbol.isType || symbol.isTrait) { + d.Type(symbol.name) + } else { + d.Term(symbol.name) + } + + Symbols.Global(previous_symbol, next_atom) } symbolsCache += (symbol -> out_symbol_path) out_symbol_path From 39f370bd0976c9fe182e8297f1ed7c7f0d0883b1 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 6 Nov 2018 21:40:47 +0100 Subject: [PATCH 10/19] add companionClass and companionModule to tastyreflect + use them to select correct symbol name --- .../dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala | 4 ++++ library/src/scala/tasty/reflect/SymbolOps.scala | 5 +++++ .../src/dotty/semanticdb/SemanticdbConsumer.scala | 10 ++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index 155dd16acbd8..f6a3a5dbf5b5 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -30,6 +30,10 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def owner(implicit ctx: Context): Symbol = symbol.owner + def companionModule(implicit ctx: Context): Symbol = symbol.companionModule + + def companionClass(implicit ctx: Context): Symbol = symbol.companionClass + def localContext(implicit ctx: Context): Context = { if (symbol.exists) ctx.withOwner(symbol) else ctx diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index 33a006be1020..57a0ca0af9fe 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -26,6 +26,11 @@ trait SymbolOps extends Core { /** The full name of this symbol up to the root package. */ def fullName(implicit ctx: Context): String + + def companionModule(implicit ctx: Context): Symbol + + def companionClass(implicit ctx: Context): Symbol + def pos(implicit ctx: Context): Position def localContext(implicit ctx: Context): Context diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index c4b63d8faf8e..44f14c9b5e6c 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -70,6 +70,8 @@ class SemanticdbConsumer extends TastyConsumer { case _ => false } + def isObject: Boolean = symbol.flags.isObject + def isTrait: Boolean = symbol.flags.isTrait def isValueParameter: Boolean = symbol.flags.isParam @@ -105,13 +107,17 @@ class SemanticdbConsumer extends TastyConsumer { val next_atom = if (symbol.isPackage) { d.Package(symbol.name) - } else if (symbol.isTypeParameter) { - d.TypeParameter(symbol.name) + } else if (symbol.isObject) { + d.Term(symbol.companionModule.name) } else if (symbol.isMethod) { d.Method(symbol.name, disimbiguate(previous_symbol + symbol.name)) } else if (symbol.isValueParameter) { d.Parameter(symbol.name) + } else if (symbol.isTypeParameter) { + d.TypeParameter(symbol.name) } else if (symbol.isType || symbol.isTrait) { + //println(symbol.name, symbol.companionClass.name, symbol.companionModule.name, symbol.flags.toString) + d.Type(symbol.name) } else { d.Term(symbol.name) From 6bc7254ab2a996b5e6cf6bcdc24b88e5c7b5c3dd Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 6 Nov 2018 22:09:52 +0100 Subject: [PATCH 11/19] fix range for package clause --- .../dotty/semanticdb/SemanticdbConsumer.scala | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 44f14c9b5e6c..3bf928a70753 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -93,6 +93,7 @@ class SemanticdbConsumer extends TastyConsumer { "()" } } + def iterateParent(symbol: Symbol): String = { if (symbolsCache.contains(symbol)) { return symbolsCache(symbol) @@ -147,7 +148,12 @@ class SemanticdbConsumer extends TastyConsumer { ) } - def range(pos: Position, name: String): s.Range = { + def range(tree: Tree, pos: Position, name: String): s.Range = { + val offset = tree match { + case IsPackageClause(tree) => "package ".length + case _ => 0 + } + val range_end_column = if (name == "") { pos.endColumn @@ -155,7 +161,7 @@ class SemanticdbConsumer extends TastyConsumer { pos.startColumn + name.length } - s.Range(pos.startLine, pos.startColumn, pos.startLine, range_end_column) + s.Range(pos.startLine, pos.startColumn + offset, pos.startLine, range_end_column + offset) } def rangeExclude(range: Position, exclude: Position): s.Range = { @@ -166,13 +172,13 @@ class SemanticdbConsumer extends TastyConsumer { range.endColumn) } - def typetreeSymbol(typetree: TypeTree): Unit = + def typetreeSymbol(tree: Tree, typetree: TypeTree): Unit = typetree match { case TypeTree.Synthetic => () case _ => addOccurence(typetree.symbol, s.SymbolOccurrence.Role.REFERENCE, - range(typetree.pos, typetree.symbol.name)) + range(tree, typetree.pos, typetree.symbol.name)) } override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { @@ -181,14 +187,14 @@ class SemanticdbConsumer extends TastyConsumer { case IsDefinition(body) => { if (tree.symbol.name != "") { val _ = tree match { - case DefDef(_, _, _, typetree, _) => typetreeSymbol(typetree) - case ValDef(_, typetree, _) => typetreeSymbol(typetree) + case DefDef(_, _, _, typetree, _) => typetreeSymbol(tree, typetree) + case ValDef(_, typetree, _) => typetreeSymbol(tree, typetree) case _ => () } } addOccurence(tree.symbol, s.SymbolOccurrence.Role.DEFINITION, - range(tree.symbol.pos, tree.symbol.name)) + range(tree, tree.symbol.pos, tree.symbol.name)) super.traverseTree(body) } @@ -204,13 +210,13 @@ class SemanticdbConsumer extends TastyConsumer { //println(tree.namePos.startColumn, tree.namePos.endColumn) addOccurence(tree.symbol, s.SymbolOccurrence.Role.REFERENCE, - range(tree.pos, tree.symbol.name)) + range(tree, tree.pos, tree.symbol.name)) super.traverseTree(tree) } case PackageClause(_) => addOccurence(tree.symbol, s.SymbolOccurrence.Role.REFERENCE, - range(tree.pos, tree.symbol.name)) + range(tree, tree.pos, tree.symbol.name)) super.traverseTree(tree) case tree => From d47ae1e71e76b96c15bf2078a91554bb79a2fcbf Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 6 Nov 2018 22:28:27 +0100 Subject: [PATCH 12/19] add special case for ident symbols of packageclause --- semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 3bf928a70753..c3b9c025a67e 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -206,11 +206,12 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Ident(_) => { - //println(tree.pos.startColumn, tree.pos.endColumn) - //println(tree.namePos.startColumn, tree.namePos.endColumn) - addOccurence(tree.symbol, + // To avoid adding the identifier of the package symbol + if (tree.symbol.owner.name != "") { + addOccurence(tree.symbol, s.SymbolOccurrence.Role.REFERENCE, range(tree, tree.pos, tree.symbol.name)) + } super.traverseTree(tree) } case PackageClause(_) => From afdabc52af33792cd8c7170f1e2940044f70a85b Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 11 Nov 2018 22:59:07 +0100 Subject: [PATCH 13/19] add tests extracted from scalameta --- .../input/src/main/scala/example/Access.scala | 11 ++ .../src/main/scala/example/Advanced.scala | 39 ++++++ .../src/main/scala/example/Anonymous.scala | 18 +++ .../src/main/scala/example/Classes.scala | 33 +++++ .../input/src/main/scala/example/Empty.scala | 9 ++ .../src/main/scala/example/EmptyObject.scala | 3 + .../src/main/scala/example/Example2.scala | 11 ++ .../src/main/scala/example/Exclude.scala | 4 + .../input/src/main/scala/example/Flags.scala | 24 ++++ .../src/main/scala/example/Imports.scala | 1 + .../src/main/scala/example/Issue1749.scala | 11 ++ .../input/src/main/scala/example/Locals.scala | 8 ++ .../main/scala/example/MacroAnnotations.scala | 5 + .../src/main/scala/example/MethodUsages.scala | 33 +++++ .../src/main/scala/example/Methods.scala | 38 ++++++ .../src/main/scala/example/Objects.scala | 5 + .../src/main/scala/example/Overrides.scala | 4 + .../src/main/scala/example/Prefixes.scala | 33 +++++ .../input/src/main/scala/example/Selfs.scala | 24 ++++ .../src/main/scala/example/Synthetic.scala | 47 +++++++ .../input/src/main/scala/example/Traits.scala | 14 +++ .../input/src/main/scala/example/Types.scala | 115 ++++++++++++++++++ .../input/src/main/scala/example/Vals.scala | 47 +++++++ .../src/main/scala/example/local-file.scala | 8 ++ 24 files changed, 545 insertions(+) create mode 100644 semanticdb/input/src/main/scala/example/Access.scala create mode 100644 semanticdb/input/src/main/scala/example/Advanced.scala create mode 100644 semanticdb/input/src/main/scala/example/Anonymous.scala create mode 100644 semanticdb/input/src/main/scala/example/Classes.scala create mode 100644 semanticdb/input/src/main/scala/example/Empty.scala create mode 100644 semanticdb/input/src/main/scala/example/EmptyObject.scala create mode 100644 semanticdb/input/src/main/scala/example/Example2.scala create mode 100644 semanticdb/input/src/main/scala/example/Exclude.scala create mode 100644 semanticdb/input/src/main/scala/example/Flags.scala create mode 100644 semanticdb/input/src/main/scala/example/Imports.scala create mode 100644 semanticdb/input/src/main/scala/example/Issue1749.scala create mode 100644 semanticdb/input/src/main/scala/example/Locals.scala create mode 100644 semanticdb/input/src/main/scala/example/MacroAnnotations.scala create mode 100644 semanticdb/input/src/main/scala/example/MethodUsages.scala create mode 100644 semanticdb/input/src/main/scala/example/Methods.scala create mode 100644 semanticdb/input/src/main/scala/example/Objects.scala create mode 100644 semanticdb/input/src/main/scala/example/Overrides.scala create mode 100644 semanticdb/input/src/main/scala/example/Prefixes.scala create mode 100644 semanticdb/input/src/main/scala/example/Selfs.scala create mode 100644 semanticdb/input/src/main/scala/example/Synthetic.scala create mode 100644 semanticdb/input/src/main/scala/example/Traits.scala create mode 100644 semanticdb/input/src/main/scala/example/Types.scala create mode 100644 semanticdb/input/src/main/scala/example/Vals.scala create mode 100644 semanticdb/input/src/main/scala/example/local-file.scala diff --git a/semanticdb/input/src/main/scala/example/Access.scala b/semanticdb/input/src/main/scala/example/Access.scala new file mode 100644 index 000000000000..aee3754da66b --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Access.scala @@ -0,0 +1,11 @@ +package example + +class Access { + private def m1 = ??? + private[this] def m2 = ??? + private[Access] def m3 = ??? + protected def m4 = ??? + protected[this] def m5 = ??? + protected[example] def m6 = ??? + def m7 = ??? +} diff --git a/semanticdb/input/src/main/scala/example/Advanced.scala b/semanticdb/input/src/main/scala/example/Advanced.scala new file mode 100644 index 000000000000..fdee5eccad19 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Advanced.scala @@ -0,0 +1,39 @@ +package example + +import scala.language.existentials +import scala.language.higherKinds +import scala.language.reflectiveCalls + +class AdvC[T] { + def t: T = ??? +} + +class Structural { + def s1: { val x: Int } = ??? + def s2 = new { val x: Int = ??? } + def s3 = new { def m(x: Int): Int = ??? } +} + +class Existential { + def e1: List[_] = ??? +} + +class AdvD[CC[B]] extends AdvC[CC[B]] + +object AdvTest { + val s = new Structural + val s1 = s.s1 + val s2 = s.s2 + val s3 = s.s3 + + val e = new Existential + val e1 = e.e1 + val e1x = e.e1.head + locally { + (??? : Any) match { + case e3: List[_] => + val e3x = e3.head + () + } + } +} diff --git a/semanticdb/input/src/main/scala/example/Anonymous.scala b/semanticdb/input/src/main/scala/example/Anonymous.scala new file mode 100644 index 000000000000..aae065fa7ae3 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Anonymous.scala @@ -0,0 +1,18 @@ +package example +import scala.language.higherKinds + +class Anonymous { + this: Anonymous => + + def m1[T[_], B] = ??? + def m2: Map[_, List[_]] = ??? + locally { + ??? match { case _: List[_] => } + } + locally { + val x: Int => Int = _ => ??? + } + + trait Foo + var x = new Foo {} +} diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala new file mode 100644 index 000000000000..b82f2c030ff0 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -0,0 +1,33 @@ +package example + +class C1(val x1: Int) extends AnyVal + +class C2(val x2: Int) extends AnyVal +object C2 + +case class C3(x: Int) + +case class C4(x: Int) +object C4 + +object M { + implicit class C5(x: Int) +} + +case class C6(private val x: Int) + +class C7(x: Int) + +class C8(private[this] val x: Int) + +class C9(private[this] var x: Int) + +object N { + val anonClass = new C7(42) { + val local = ??? + } + val anonFun = List(1).map { i => + val local = 2 + local + 2 + } +} diff --git a/semanticdb/input/src/main/scala/example/Empty.scala b/semanticdb/input/src/main/scala/example/Empty.scala new file mode 100644 index 000000000000..eac850705562 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Empty.scala @@ -0,0 +1,9 @@ +package example + +class AdvA { + def b: AdvB = ??? +} + +class AdvB { + def a: AdvA = ??? +} diff --git a/semanticdb/input/src/main/scala/example/EmptyObject.scala b/semanticdb/input/src/main/scala/example/EmptyObject.scala new file mode 100644 index 000000000000..58773e9bd7eb --- /dev/null +++ b/semanticdb/input/src/main/scala/example/EmptyObject.scala @@ -0,0 +1,3 @@ +package example + +object EmptyObject {} diff --git a/semanticdb/input/src/main/scala/example/Example2.scala b/semanticdb/input/src/main/scala/example/Example2.scala new file mode 100644 index 000000000000..900d0cbbd1d6 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Example2.scala @@ -0,0 +1,11 @@ +package example + +import scala.concurrent.Future + +object OExample { self => + new scala.collection.mutable.Stack[Int]() + def main(args: Array[String]): Unit = { + println(1) + } + val x = scala.reflect.classTag[Int] +} diff --git a/semanticdb/input/src/main/scala/example/Exclude.scala b/semanticdb/input/src/main/scala/example/Exclude.scala new file mode 100644 index 000000000000..35e085572ff5 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Exclude.scala @@ -0,0 +1,4 @@ +package example + +// This class should be excluded by semanticdb. +class Exclude diff --git a/semanticdb/input/src/main/scala/example/Flags.scala b/semanticdb/input/src/main/scala/example/Flags.scala new file mode 100644 index 000000000000..51c56d2fac47 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Flags.scala @@ -0,0 +1,24 @@ +package example + +package object p { + private lazy val x = 1 + protected implicit var y: Int = 2 + def z(pp: Int) = 3 + def m[TT] = ??? + abstract class C[+T, -U, V](x: T, y: U, z: V) { + def this() = this(???, ???, ???) + def w: Int + } + type T1 = Int + type T2[T] = S[T] + type U <: Int + type V >: Int + case object X + final class Y + sealed trait Z + class AA(x: Int, val y: Int, var z: Int) + class S[@specialized T] + val List(xs1) = ??? + ??? match { case List(xs2) => ??? } + ??? match { case _: List[t] => ??? } +} diff --git a/semanticdb/input/src/main/scala/example/Imports.scala b/semanticdb/input/src/main/scala/example/Imports.scala new file mode 100644 index 000000000000..e6a2bef1a5c4 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Imports.scala @@ -0,0 +1 @@ +import scala.util.control.NonFatal diff --git a/semanticdb/input/src/main/scala/example/Issue1749.scala b/semanticdb/input/src/main/scala/example/Issue1749.scala new file mode 100644 index 000000000000..66f35aa00c88 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Issue1749.scala @@ -0,0 +1,11 @@ +// See https://github.com/scalameta/scalameta/issues/1749 +package example + +import scala.math.Ordered.orderingToOrdered + +class Issue1749 { + val x1 = 42 + val x2 = 42 + (x1, x1) + .compare((x2, x2)) +} diff --git a/semanticdb/input/src/main/scala/example/Locals.scala b/semanticdb/input/src/main/scala/example/Locals.scala new file mode 100644 index 000000000000..308a1e713846 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Locals.scala @@ -0,0 +1,8 @@ +package example + +object Test { + val xs = { + val x = 42 + List(x) + } +} diff --git a/semanticdb/input/src/main/scala/example/MacroAnnotations.scala b/semanticdb/input/src/main/scala/example/MacroAnnotations.scala new file mode 100644 index 000000000000..02276ed2ef6f --- /dev/null +++ b/semanticdb/input/src/main/scala/example/MacroAnnotations.scala @@ -0,0 +1,5 @@ +package example + +//@MacroAnnotation +class MacroAnnotations +object MacroAnnotations diff --git a/semanticdb/input/src/main/scala/example/MethodUsages.scala b/semanticdb/input/src/main/scala/example/MethodUsages.scala new file mode 100644 index 000000000000..ea0b9e93713f --- /dev/null +++ b/semanticdb/input/src/main/scala/example/MethodUsages.scala @@ -0,0 +1,33 @@ +package example + +class MethodUsages { + val m = new Methods[Int] + m.m1 + m.m2() + m.m3(0) + m.m4(0)(0) + m.m5("") + m.m5(0) + m.m6(0) + m.m6(new m.List[Int]) + m.m6(Nil) + m.m7a(m, new m.List[Int]) + m.m7b(new m.List[Int]) + m.`m8().`() + m.m9(null) + m.m10(null) + m.m11(Predef) + m.m11(OExample) + m.m12a(null) + m.m12b(null) + m.m13(0) + m.m15(0) + m.m16(0) + m.m16(0) + m.m17.m() + m.m17(1) + m.m17("") + m.m18.m() + m.m18(1) + m.m18("") +} diff --git a/semanticdb/input/src/main/scala/example/Methods.scala b/semanticdb/input/src/main/scala/example/Methods.scala new file mode 100644 index 000000000000..97af95b3c9c2 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Methods.scala @@ -0,0 +1,38 @@ +package example + +import scala.math.Ordering +import scala.language.existentials + +class Methods[T] { + class List[T] + type AList[T] = List[T] + def m1 = ??? + def m2() = ??? + def m3(x: Int) = ??? + def m4(x: Int)(y: Int) = ??? + def m5(x: String) = ??? + def m5(x: Int) = ??? + def m6(x: Int) = ??? + def m6(x: List[T]) = ??? + def m6(x: scala.List[T]) = ??? + def m7a[U: Ordering](c: Methods[T], l: List[U]) = ??? + def m7b[U <: T](l: List[U]) = ??? + def `m8().`() = ??? + class `m9().` + def m9(x: `m9().`) = ??? + def m10(x: AList[T]) = ??? + def m11(x: Predef.type) = ??? + def m11(x: OExample.type) = ??? + def m12a(x: {}) = ??? + def m12b(x: { val x: Int }) = ??? + def m13(x: Int @unchecked) = ??? + def m15(x: => Int) = ??? + def m16(x: Int*) = ??? + object m17 { def m() = ??? } + def m17(a: Int) = ??? + def m17(b: String) = ??? + val m18 = m17 + def m18(a: Int) = ??? + def m18(b: String) = ??? + def m19(x: Int, y: Int = 2)(z: Int = 3) = ??? +} diff --git a/semanticdb/input/src/main/scala/example/Objects.scala b/semanticdb/input/src/main/scala/example/Objects.scala new file mode 100644 index 000000000000..69fba5dfed3c --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Objects.scala @@ -0,0 +1,5 @@ +package example + +object X { + object Y +} diff --git a/semanticdb/input/src/main/scala/example/Overrides.scala b/semanticdb/input/src/main/scala/example/Overrides.scala new file mode 100644 index 000000000000..3d13cf4823d8 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Overrides.scala @@ -0,0 +1,4 @@ +package example + +trait OveA { def foo: Int } +class OveB() extends OveA { def foo: Int = 2 } diff --git a/semanticdb/input/src/main/scala/example/Prefixes.scala b/semanticdb/input/src/main/scala/example/Prefixes.scala new file mode 100644 index 000000000000..10daaede1937 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Prefixes.scala @@ -0,0 +1,33 @@ +package example + +class PrefC { + type T + def m1: T = ??? + + object N { + type U + } + def k1: N.U = ??? +} + +object PrefM { + type T + def n1: T = ??? +} + +object PrefO extends PrefC { + def o1: T = ??? +} + +object PrefTest { + val c: PrefC = ??? + def m2: c.T = ??? + def k2: c.N.U = ??? + import c.N._ + def k3: U = ??? + + def n2: PrefM.T = ??? + + import PrefM._ + def n3: T = ??? +} diff --git a/semanticdb/input/src/main/scala/example/Selfs.scala b/semanticdb/input/src/main/scala/example/Selfs.scala new file mode 100644 index 000000000000..06ed2c26709a --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Selfs.scala @@ -0,0 +1,24 @@ +package example + +class B + +class AC1 extends B { self => +} + +class AC2 extends B { self: B => +} + +abstract class AC3 extends B { self: B with Int => +} + +class AC4 extends B { a => +} + +class AC5 extends B { a : B => +} + +class AC6 extends B { this: B => +} + +abstract class AC7 { a: B => +} diff --git a/semanticdb/input/src/main/scala/example/Synthetic.scala b/semanticdb/input/src/main/scala/example/Synthetic.scala new file mode 100644 index 000000000000..c888edc329e8 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Synthetic.scala @@ -0,0 +1,47 @@ +package example + +class Synthetic { + List(1).map(_ + 2) + Array.empty[Int].headOption + "fooo".stripPrefix("o") + + // See https://github.com/scalameta/scalameta/issues/977 + val Name = "name:(.*)".r + val x #:: xs = Stream(1, 2) + val Name(name) = "name:foo" + 1 #:: 2 #:: Stream.empty + + val lst = 1 #:: 2 #:: Stream.empty + lst + "foo" + + for (x <- 1 to 10; y <- 0 until 10) println(x -> x) + for (i <- 1 to 10; j <- 0 until 10) yield (i, j) + for (i <- 1 to 10; j <- 0 until 10 if i % 2 == 0) yield (i, j) + + object s { + def apply() = 2 + s() + s.apply() + case class Bar() + Bar() + null.asInstanceOf[Int => Int](2) + } + + class J[T: Manifest] { val arr = Array.empty[T] } + + class F + implicit val ordering: Ordering[F] = ??? + val f: Ordered[F] = new F + + import scala.concurrent.ExecutionContext.Implicits.global + for { + a <- scala.concurrent.Future.successful(1) + b <- scala.concurrent.Future.successful(2) + } println(a) + for { + a <- scala.concurrent.Future.successful(1) + b <- scala.concurrent.Future.successful(2) + if a < b + } yield a + +} diff --git a/semanticdb/input/src/main/scala/example/Traits.scala b/semanticdb/input/src/main/scala/example/Traits.scala new file mode 100644 index 000000000000..fe857fbf7be3 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Traits.scala @@ -0,0 +1,14 @@ +package example + +trait T { + def x = 2 +} + +sealed trait U +object U { + def u: U = new U {} +} + +class C +trait V { self: C => +} diff --git a/semanticdb/input/src/main/scala/example/Types.scala b/semanticdb/input/src/main/scala/example/Types.scala new file mode 100644 index 000000000000..a22b49115ead --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Types.scala @@ -0,0 +1,115 @@ +package example + +import scala.language.existentials +import scala.language.higherKinds + +class ann[T](x: T) extends scala.annotation.StaticAnnotation +class ann1 extends scala.annotation.StaticAnnotation +class ann2 extends scala.annotation.StaticAnnotation + +class TypB + +class TypC + +class TypP { + class C + class X + val x = new X +} + +class TypT { + class C + class X + val x = new X +} + +object TypTest { + class M { + def m: Int = ??? + } + + trait N { + def n: Int = ??? + } + + class C extends M { + val p = new TypP + val x = p.x + + val typeRef1: C = ??? + val typeRef2: p.C = ??? + val typeRef3: TypT#C = ??? + val typeRef4: List[Int] = ??? + + val singleType1: x.type = ??? + val singleType2: p.x.type = ??? + val Either = scala.util.Either + + val thisType1: this.type = ??? + val thisType2: C.this.type = ??? + + val superType1 = super.m + val superType2 = super[M].m + val superType3 = C.super[M].m + + val compoundType1: { def k: Int } = ??? + val compoundType2: M with N = ??? + val compoundType3: M with N { def k: Int } = ??? + val compoundType4 = new { def k: Int = ??? } + val compoundType5 = new M with N + val compoundType6 = new M with N { def k: Int = ??? } + + val annType1: T @ann(42) = ??? + val annType2: T @ann1 @ann2 = ??? + + val existentialType2: List[_] = ??? + val existentialType3 = Class.forName("foo.Bar") + val existentialType4 = Class.forName("foo.Bar") + + def typeLambda1[M[_]] = ??? + typeLambda1[({ type L[T] = List[T] })#L] + + object ClassInfoType1 + class ClassInfoType2 extends B { def x = 42 } + trait ClassInfoType3[T] + + object MethodType { + def x1: Int = ??? + def x2: Int = ??? + def m3: Int = ??? + def m4(): Int = ??? + def m5(x: Int): Int = ??? + def m6[T](x: T): T = ??? + } + + object ByNameType { + def m1(x: => Int): Int = ??? + } + + case class RepeatedType(s: String*) { + def m1(x: Int*): Int = s.length + } + + object TypeType { + type T1 + def m2[T2 >: C <: C] = ??? + def m3[M3[_]] = ??? + type T4 = C + type T5[U] = U + } + } + + object Literal { + final val int = 1 + final val long = 1L + final val float = 1f + final val double = 2d + final val nil = null + final val char = 'a' + final val string = "a" + final val bool = true + final val unit = () + final val javaEnum = java.nio.file.LinkOption.NOFOLLOW_LINKS + final val clazzOf = classOf[Option[Int]] + } +} diff --git a/semanticdb/input/src/main/scala/example/Vals.scala b/semanticdb/input/src/main/scala/example/Vals.scala new file mode 100644 index 000000000000..64e8026448bc --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Vals.scala @@ -0,0 +1,47 @@ +package example + +abstract class Vals(p: Int, val xp: Int, var yp: Int) { + val xm: Int = ??? + val xam: Int + private[this] val xlm: Int = ??? + lazy val xzm: Int = ??? + private[this] lazy val xzlm: Int = ??? + final val xfm: Int = ??? + implicit val xim: Int = ??? + var ym: Int = ??? + var yam: Int + private[this] var ylm: Int = ??? + // NOTE: lazy not allowed here. Only vals can be lazy + // lazy var xzm: Int = ??? + // private[this] lazy var yzlm: Int = ??? + final var yfm: Int = ??? + implicit var yim: Int = ??? + def m = { + val xl: Int = ??? + lazy val xzl: Int = ??? + // NOTE: local values cannot be final + // final val xfl: Int = ??? + implicit val xil: Int = ??? + var yl: Int = ??? + // NOTE: lazy not allowed here. Only vals can be lazy + // lazy var yzl: Int = ??? + // NOTE: local variables cannot be final + // final var yfl: Int = ??? + implicit var yil: Int = ??? + yl = xl + yl + println(xzl) + yil = xil + yil + } + println(xzlm) + ylm = xlm + ylm +} + +object ValUsages { + val v: Vals = ??? + v.yp = v.xp + v.yp + v.ym = v.xm + v.ym + v.yam = v.xam + v.yam + println(v.xzm) + v.yfm = v.xfm + v.yfm + v.yim = v.xim + v.yim +} diff --git a/semanticdb/input/src/main/scala/example/local-file.scala b/semanticdb/input/src/main/scala/example/local-file.scala new file mode 100644 index 000000000000..ea699bdfff57 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/local-file.scala @@ -0,0 +1,8 @@ +package example + +class `local-file` { + locally { + val local = 42 + local + 4 + } +} From e0815a921f20f2d8e419e087d107ad4bd879a33b Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 12 Nov 2018 18:45:25 +0100 Subject: [PATCH 14/19] fix symbols and add method to distinguish compiler generated constructs --- .../TypeOrBoundsTreesOpsImpl.scala | 1 - .../src/main/scala/example/Example.scala | 2 +- .../src/main/scala/example/Example2.scala | 1 - .../src/main/scala/example/Imports.scala | 2 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 159 +++++++++++++----- 5 files changed, 116 insertions(+), 49 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala index 673c1a640d3f..1622844052e2 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala @@ -20,7 +20,6 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w def pos(implicit ctx: Context): Position = tpt.pos def symbol(implicit ctx: Context): Symbol = tpt.symbol def tpe(implicit ctx: Context): Type = tpt.tpe.stripTypeVar - def symbol(implicit ctx: Context): Symbol = tpt.symbol } object IsTypeTree extends IsTypeTreeExtractor { diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index 00b34748ae19..9c8cd8e4f862 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -14,4 +14,4 @@ class Example { x + y ) -} +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Example2.scala b/semanticdb/input/src/main/scala/example/Example2.scala index 900d0cbbd1d6..1135020e76db 100644 --- a/semanticdb/input/src/main/scala/example/Example2.scala +++ b/semanticdb/input/src/main/scala/example/Example2.scala @@ -3,7 +3,6 @@ package example import scala.concurrent.Future object OExample { self => - new scala.collection.mutable.Stack[Int]() def main(args: Array[String]): Unit = { println(1) } diff --git a/semanticdb/input/src/main/scala/example/Imports.scala b/semanticdb/input/src/main/scala/example/Imports.scala index e6a2bef1a5c4..5f4ead1f23cc 100644 --- a/semanticdb/input/src/main/scala/example/Imports.scala +++ b/semanticdb/input/src/main/scala/example/Imports.scala @@ -1 +1 @@ -import scala.util.control.NonFatal +//import scala.util.control.NonFatal diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index c3b9c025a67e..3fbb4c44c3c7 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -45,9 +45,44 @@ class SemanticdbConsumer extends TastyConsumer { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ + + object ChildTraverser extends TreeTraverser { + var depth: Int = 0 + var children: List[Tree] = Nil + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { + if (depth == 0) { + depth = depth + 1 + super.traverseTree(tree) + depth = depth - 1 + } else { + children = tree :: children + } + } + def getChildren(tree: Tree)(implicit ctx: Context): List[Position] = { + children = Nil + depth = 0 + traverseTree(tree)(ctx) + return children.map(_.pos) + } + } + object Traverser extends TreeTraverser { val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() + implicit class TreeExtender(tree: Tree) { + def isUserCreated: Boolean = { + val children: List[Position] = + ChildTraverser.getChildren(tree)(tasty.rootContext) + return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children + .exists(_ == tree.pos)) + } + } + + implicit class TypeTreeExtender(tree: TypeTree) { + def isUserCreated: Boolean = { + return !(tree.pos.exists && tree.pos.start == tree.pos.end) + } + } implicit class SymbolExtender(symbol: Symbol) { def isTypeParameter: Boolean = symbol match { @@ -57,17 +92,17 @@ class SemanticdbConsumer extends TastyConsumer { def isType: Boolean = symbol match { case IsTypeSymbol(_) => true - case _ => false + case _ => false } def isMethod: Boolean = symbol match { case IsDefSymbol(_) => true - case _ => false + case _ => false } def isPackage: Boolean = symbol match { case IsPackageSymbol(_) => true - case _ => false + case _ => false } def isObject: Boolean = symbol.flags.isObject @@ -106,23 +141,24 @@ class SemanticdbConsumer extends TastyConsumer { } else { val previous_symbol = iterateParent(symbol.owner) val next_atom = - if (symbol.isPackage) { - d.Package(symbol.name) - } else if (symbol.isObject) { - d.Term(symbol.companionModule.name) - } else if (symbol.isMethod) { - d.Method(symbol.name, disimbiguate(previous_symbol + symbol.name)) - } else if (symbol.isValueParameter) { - d.Parameter(symbol.name) - } else if (symbol.isTypeParameter) { - d.TypeParameter(symbol.name) - } else if (symbol.isType || symbol.isTrait) { - //println(symbol.name, symbol.companionClass.name, symbol.companionModule.name, symbol.flags.toString) - - d.Type(symbol.name) - } else { - d.Term(symbol.name) - } + if (symbol.isPackage) { + d.Package(symbol.name) + } else if (symbol.isObject) { + d.Term(symbol.companionModule.name) + } else if (symbol.isMethod) { + d.Method(symbol.name, + disimbiguate(previous_symbol + symbol.name)) + } else if (symbol.isValueParameter) { + d.Parameter(symbol.name) + } else if (symbol.isTypeParameter) { + d.TypeParameter(symbol.name) + } else if (symbol.isType || symbol.isTrait) { + //println(symbol.name, symbol.companionClass.name, symbol.companionModule.name, symbol.flags.toString) + + d.Type(symbol.name) + } else { + d.Term(symbol.name) + } Symbols.Global(previous_symbol, next_atom) } @@ -134,10 +170,8 @@ class SemanticdbConsumer extends TastyConsumer { def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - //if (symbolsCache.contains(symbol)) return - val symbol_path = iterateParent(symbol) - if (symbol_path == "" || symbol.name == "") return + if (symbol_path == "") return occurrences = occurrences :+ @@ -146,12 +180,29 @@ class SemanticdbConsumer extends TastyConsumer { symbol_path, type_symbol ) + + } + + def addOccurenceTree(tree: Tree, + type_symbol: s.SymbolOccurrence.Role, + range: s.Range, + force_add: Boolean = false): Unit = { + if (tree.isUserCreated || force_add) { + addOccurence(tree.symbol, type_symbol, range) + } + } + def addOccurenceTypeTree(typetree: TypeTree, + type_symbol: s.SymbolOccurrence.Role, + range: s.Range): Unit = { + if (typetree.isUserCreated) { + addOccurence(typetree.symbol, type_symbol, range) + } } def range(tree: Tree, pos: Position, name: String): s.Range = { val offset = tree match { - case IsPackageClause(tree) => "package ".length - case _ => 0 + case IsPackageClause(tree) => "package ".length + case _ => 0 } val range_end_column = @@ -161,7 +212,10 @@ class SemanticdbConsumer extends TastyConsumer { pos.startColumn + name.length } - s.Range(pos.startLine, pos.startColumn + offset, pos.startLine, range_end_column + offset) + s.Range(pos.startLine, + pos.startColumn + offset, + pos.startLine, + range_end_column + offset) } def rangeExclude(range: Position, exclude: Position): s.Range = { @@ -176,48 +230,63 @@ class SemanticdbConsumer extends TastyConsumer { typetree match { case TypeTree.Synthetic => () case _ => - addOccurence(typetree.symbol, - s.SymbolOccurrence.Role.REFERENCE, - range(tree, typetree.pos, typetree.symbol.name)) + addOccurenceTypeTree( + typetree, + s.SymbolOccurrence.Role.REFERENCE, + range(tree, typetree.pos, typetree.symbol.name)) } override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) + println(tree) tree match { case IsDefinition(body) => { - if (tree.symbol.name != "") { - val _ = tree match { - case DefDef(_, _, _, typetree, _) => typetreeSymbol(tree, typetree) - case ValDef(_, typetree, _) => typetreeSymbol(tree, typetree) - case _ => () - } + tree match { + case DefDef(name, _, _, typetree, _) => + typetreeSymbol(tree, typetree) + case ValDef(_, typetree, _) => + typetreeSymbol(tree, typetree) + case _ => () } - addOccurence(tree.symbol, - s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.name)) + val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) + if (tree.symbol.name == "" && !tree.isUserCreated) { + val range_symbol2 = s.Range(range_symbol.startLine, + range_symbol.startCharacter - 4, + range_symbol.endLine, + range_symbol.endCharacter - 4) + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range_symbol2, + true) + + } else { + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range_symbol) + } super.traverseTree(body) } case Term.Select(qualifier, _, _) => { val range = rangeExclude(tree.pos, qualifier.pos) - addOccurence(tree.symbol, s.SymbolOccurrence.Role.REFERENCE, range) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTree(tree) } case Term.Ident(_) => { // To avoid adding the identifier of the package symbol if (tree.symbol.owner.name != "") { - addOccurence(tree.symbol, - s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.name)) + addOccurenceTree(tree, + s.SymbolOccurrence.Role.REFERENCE, + range(tree, tree.pos, tree.symbol.name)) } super.traverseTree(tree) } case PackageClause(_) => - addOccurence(tree.symbol, - s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.name)) + addOccurenceTree(tree, + s.SymbolOccurrence.Role.REFERENCE, + range(tree, tree.pos, tree.symbol.name)) super.traverseTree(tree) case tree => From 31ca054a9b02ca630a018e4503a724d326f26c46 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 13 Nov 2018 00:08:48 +0100 Subject: [PATCH 15/19] fix build errors due to a bad rebase --- .../dotty/semanticdb/SemanticdbConsumer.scala | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 3fbb4c44c3c7..6e3fedfd6109 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -3,7 +3,6 @@ package dotty.semanticdb import scala.tasty.Reflection import scala.tasty.file.TastyConsumer -import scala.tasty.util.TreeTraverser import dotty.tools.dotc.tastyreflect import scala.collection.mutable.HashMap import scala.meta.internal.{semanticdb => s} @@ -13,28 +12,6 @@ import dotty.semanticdb.Scala._ class SemanticdbConsumer extends TastyConsumer { var stack: List[String] = Nil - /* - val symbolsDefs : HashMap[String, Int] = HashMap() - val symbolsVals : HashMap[String, Int] = HashMap() - - def insertPathDefDef(path: String): String = { - if (symbolsDefs.contains(path)) { - symbolsDefs += (path -> (symbolsDefs(path) + 1)) - "+" + (symbolsDefs(path) - 1) - } else { - symbolsDefs += (path -> 1) - "" - } - } - def insertPathValDef(path: String): String = { - if (symbolsVals.contains(path)) { - symbolsVals += (path -> (symbolsVals(path) + 1)) - "+" + (symbolsVals(path) - 1) - } else { - symbolsVals += (path -> 1) - "" - } - }*/ val semantic: s.TextDocument = s.TextDocument() var occurrences: Seq[s.SymbolOccurrence] = Seq() @@ -42,7 +19,6 @@ class SemanticdbConsumer extends TastyConsumer { s.TextDocument(text = text, occurrences = occurrences) } - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -67,12 +43,12 @@ class SemanticdbConsumer extends TastyConsumer { } object Traverser extends TreeTraverser { - val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() + val symbolsCache: HashMap[Symbol, String] = HashMap() val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() implicit class TreeExtender(tree: Tree) { def isUserCreated: Boolean = { val children: List[Position] = - ChildTraverser.getChildren(tree)(tasty.rootContext) + ChildTraverser.getChildren(tree)(reflect.rootContext) return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children .exists(_ == tree.pos)) } @@ -115,9 +91,6 @@ class SemanticdbConsumer extends TastyConsumer { def isJavaClass: Boolean = false } - val symbolsCache: HashMap[tasty.Symbol, String] = HashMap() - val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() - def disimbiguate(symbol_path: String): String = { if (symbolPathsDisimbiguator.contains(symbol_path)) { symbolPathsDisimbiguator += @@ -238,7 +211,6 @@ class SemanticdbConsumer extends TastyConsumer { override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) - println(tree) tree match { case IsDefinition(body) => { tree match { From 3f787037584ae0780201dd6095394fd21c608356 Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 15 Nov 2018 15:47:40 +0100 Subject: [PATCH 16/19] remove companionClass and companionModule. Updated readme. Refactored getchildren --- .../dotc/tastyreflect/SymbolOpsImpl.scala | 4 --- .../src/scala/tasty/reflect/SymbolOps.scala | 5 ---- semanticdb/Makefile | 2 +- semanticdb/readme.md | 8 +++++ .../dotty/semanticdb/SemanticdbConsumer.scala | 29 +++++++++---------- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index f6a3a5dbf5b5..155dd16acbd8 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -30,10 +30,6 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def owner(implicit ctx: Context): Symbol = symbol.owner - def companionModule(implicit ctx: Context): Symbol = symbol.companionModule - - def companionClass(implicit ctx: Context): Symbol = symbol.companionClass - def localContext(implicit ctx: Context): Context = { if (symbol.exists) ctx.withOwner(symbol) else ctx diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index 57a0ca0af9fe..33a006be1020 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -26,11 +26,6 @@ trait SymbolOps extends Core { /** The full name of this symbol up to the root package. */ def fullName(implicit ctx: Context): String - - def companionModule(implicit ctx: Context): Symbol - - def companionClass(implicit ctx: Context): Symbol - def pos(implicit ctx: Context): Position def localContext(implicit ctx: Context): Context diff --git a/semanticdb/Makefile b/semanticdb/Makefile index dcf105eecb4e..1857649426d5 100644 --- a/semanticdb/Makefile +++ b/semanticdb/Makefile @@ -8,4 +8,4 @@ test: all cd ..; sbt dotty-semanticdb/test; cd - %.tasty: $(files) - cd $(dir $^); $(CURDIR)/../bin/dotc $(notdir $^) + cd $(dir $<); $(CURDIR)/../bin/dotc $(notdir $^) diff --git a/semanticdb/readme.md b/semanticdb/readme.md index f93d37a3d009..b5b73ae48f96 100644 --- a/semanticdb/readme.md +++ b/semanticdb/readme.md @@ -10,6 +10,14 @@ sbt > compile ``` +You will also need to generate the tasty files for the test. For now, you can +easily do it by executing the makefile in this folder: +``` +make +``` +Please note that the makefile will also compile the `semanticdb/input` project. + + In the second terminal, run `sbt dotty-semanticdb/test` from the main dotty build diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 6e3fedfd6109..b3ca5152e56a 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -23,22 +23,16 @@ class SemanticdbConsumer extends TastyConsumer { import reflect._ object ChildTraverser extends TreeTraverser { - var depth: Int = 0 var children: List[Tree] = Nil - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - if (depth == 0) { - depth = depth + 1 - super.traverseTree(tree) - depth = depth - 1 - } else { - children = tree :: children - } - } - def getChildren(tree: Tree)(implicit ctx: Context): List[Position] = { + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = children = tree::children + override def traversePattern(pattern: Pattern)(implicit ctx: Context): Unit = () + override def traverseTypeTree(tree: TypeOrBoundsTree)(implicit ctx: Context): Unit = () + override def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = () + + def getChildren(tree: Tree)(implicit ctx: Context): List[Tree] = { children = Nil - depth = 0 - traverseTree(tree)(ctx) - return children.map(_.pos) + traverseTreeChildren(tree)(ctx) + return children } } @@ -48,7 +42,7 @@ class SemanticdbConsumer extends TastyConsumer { implicit class TreeExtender(tree: Tree) { def isUserCreated: Boolean = { val children: List[Position] = - ChildTraverser.getChildren(tree)(reflect.rootContext) + ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children .exists(_ == tree.pos)) } @@ -117,7 +111,10 @@ class SemanticdbConsumer extends TastyConsumer { if (symbol.isPackage) { d.Package(symbol.name) } else if (symbol.isObject) { - d.Term(symbol.companionModule.name) + symbol.asClass.companionModule match { + case Some(module) => d.Term(module.name) + case _ => d.Term(symbol.name) + } } else if (symbol.isMethod) { d.Method(symbol.name, disimbiguate(previous_symbol + symbol.name)) From eeb6df75771aa3e0b77ff2b88d21dbccfd1a18ec Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 15 Nov 2018 17:22:25 +0100 Subject: [PATCH 17/19] more generic disimbiguator using tastyreflect 'methods' function --- .../dotty/semanticdb/SemanticdbConsumer.scala | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index b3ca5152e56a..96b48582ebb5 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -38,7 +38,6 @@ class SemanticdbConsumer extends TastyConsumer { object Traverser extends TreeTraverser { val symbolsCache: HashMap[Symbol, String] = HashMap() - val symbolPathsDisimbiguator: HashMap[String, Int] = HashMap() implicit class TreeExtender(tree: Tree) { def isUserCreated: Boolean = { val children: List[Position] = @@ -85,14 +84,29 @@ class SemanticdbConsumer extends TastyConsumer { def isJavaClass: Boolean = false } - def disimbiguate(symbol_path: String): String = { - if (symbolPathsDisimbiguator.contains(symbol_path)) { - symbolPathsDisimbiguator += - (symbol_path -> (symbolPathsDisimbiguator(symbol_path) + 1)) - "(+" + (symbolPathsDisimbiguator(symbol_path) - 1) + ")" - } else { - symbolPathsDisimbiguator += (symbol_path -> 1) + def resolveClass(symbol: ClassSymbol): Symbol = + (symbol.companionClass, symbol.companionModule) match { + case (_, Some(module)) if symbol.flags.isObject => module + case (Some(c), _) => c + case _ => symbol + } + + def disimbiguate(symbol_path: String, symbol: Symbol): String = { + val symbolcl = resolveClass(symbol.owner.asClass) + val methods = symbolcl.asClass.method(symbol.name) + val (methods_count, method_pos) = + methods.foldLeft((0, -1))((x:Tuple2[Int, Int], m:Symbol) => { + if (m == symbol) + (x._1+1, x._1) + else + (x._1+1, x._2) + }) + val real_pos = methods_count - method_pos - 1 + + if (real_pos == 0) { "()" + } else { + "(+" + real_pos + ")" } } @@ -111,20 +125,15 @@ class SemanticdbConsumer extends TastyConsumer { if (symbol.isPackage) { d.Package(symbol.name) } else if (symbol.isObject) { - symbol.asClass.companionModule match { - case Some(module) => d.Term(module.name) - case _ => d.Term(symbol.name) - } + d.Term(resolveClass(symbol.asClass).name) } else if (symbol.isMethod) { d.Method(symbol.name, - disimbiguate(previous_symbol + symbol.name)) + disimbiguate(previous_symbol + symbol.name, symbol)) } else if (symbol.isValueParameter) { d.Parameter(symbol.name) } else if (symbol.isTypeParameter) { d.TypeParameter(symbol.name) } else if (symbol.isType || symbol.isTrait) { - //println(symbol.name, symbol.companionClass.name, symbol.companionModule.name, symbol.flags.toString) - d.Type(symbol.name) } else { d.Term(symbol.name) From 4a5314d31ddbb3bb883e0de58318b9291a4a9695 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 20 Nov 2018 12:17:39 +0100 Subject: [PATCH 18/19] Remove the makefile and refactored the testsuite - the testsuite now builds a map sourcefile -> tastyfile, thus allowing a test to possess several class in the same source code --- .../src/scala/tasty/reflect/TreeUtils.scala | 1 + project/Build.scala | 9 +- semanticdb/Makefile | 11 -- .../src/main/scala/example/Example.scala | 4 + semanticdb/readme.md | 8 - .../dotty/semanticdb/SemanticdbConsumer.scala | 44 ++++-- semanticdb/test/dotty/semanticdb/Tests.scala | 137 +++++++++++++++--- 7 files changed, 156 insertions(+), 58 deletions(-) delete mode 100644 semanticdb/Makefile diff --git a/library/src/scala/tasty/reflect/TreeUtils.scala b/library/src/scala/tasty/reflect/TreeUtils.scala index 7f8c72b8fab4..279675c56d8a 100644 --- a/library/src/scala/tasty/reflect/TreeUtils.scala +++ b/library/src/scala/tasty/reflect/TreeUtils.scala @@ -104,6 +104,7 @@ trait TreeUtils case TypeTree.Block(typedefs, tpt) => foldTypeTree(foldTrees(x, typedefs), tpt) case TypeTree.MatchType(boundopt, selector, cases) => foldTypeCaseDefs(foldTypeTree(boundopt.fold(x)(foldTypeTree(x, _)), selector), cases) + case SyntheticBounds() => x case TypeBoundsTree(lo, hi) => foldTypeTree(foldTypeTree(x, lo), hi) } diff --git a/project/Build.scala b/project/Build.scala index dda58196c033..07b7e30a9dc4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -393,12 +393,9 @@ object Build { baseDirectory in Test := baseDirectory.value / "..", unmanagedSourceDirectories in Test += baseDirectory.value / "input" / "src" / "main" / "scala", libraryDependencies ++= List( - ("org.scalameta" %% "semanticdb" % "4.0.0" % Compile).withDottyCompat(scalaVersion.value), - "com.novocode" % "junit-interface" % "0.11" % Compile, - "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % Compile, - ("org.scalameta" %% "semanticdb" % "4.0.0" % Test).withDottyCompat(scalaVersion.value), - "com.novocode" % "junit-interface" % "0.11" % Test, - "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % Test + ("org.scalameta" %% "semanticdb" % "4.0.0").withDottyCompat(scalaVersion.value), + "com.novocode" % "junit-interface" % "0.11", + "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" ) ) diff --git a/semanticdb/Makefile b/semanticdb/Makefile deleted file mode 100644 index 1857649426d5..000000000000 --- a/semanticdb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -path="input/src/main/scala/" -files=$(shell find $(path) -type f -name '*.scala') - -all: %.tasty - cd input; sbt compile - -test: all - cd ..; sbt dotty-semanticdb/test; cd - - -%.tasty: $(files) - cd $(dir $<); $(CURDIR)/../bin/dotc $(notdir $^) diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index 9c8cd8e4f862..f4eb0fbeb947 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -14,4 +14,8 @@ class Example { x + y ) +} + +class ExampleInit() { + } \ No newline at end of file diff --git a/semanticdb/readme.md b/semanticdb/readme.md index b5b73ae48f96..f93d37a3d009 100644 --- a/semanticdb/readme.md +++ b/semanticdb/readme.md @@ -10,14 +10,6 @@ sbt > compile ``` -You will also need to generate the tasty files for the test. For now, you can -easily do it by executing the makefile in this folder: -``` -make -``` -Please note that the makefile will also compile the `semanticdb/input` project. - - In the second terminal, run `sbt dotty-semanticdb/test` from the main dotty build diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 96b48582ebb5..014c29d51030 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -5,6 +5,7 @@ import scala.tasty.file.TastyConsumer import dotty.tools.dotc.tastyreflect import scala.collection.mutable.HashMap +import scala.collection.mutable.Set import scala.meta.internal.{semanticdb => s} import dotty.semanticdb.Scala.{Descriptor => d} import dotty.semanticdb.Scala._ @@ -18,16 +19,25 @@ class SemanticdbConsumer extends TastyConsumer { def toSemanticdb(text: String): s.TextDocument = { s.TextDocument(text = text, occurrences = occurrences) } + val package_definitions: Set[String] = Set() final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ + val symbolsCache: HashMap[Symbol, String] = HashMap() + object ChildTraverser extends TreeTraverser { var children: List[Tree] = Nil - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = children = tree::children - override def traversePattern(pattern: Pattern)(implicit ctx: Context): Unit = () - override def traverseTypeTree(tree: TypeOrBoundsTree)(implicit ctx: Context): Unit = () - override def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = () + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = + children = tree :: children + override def traversePattern(pattern: Pattern)( + implicit ctx: Context): Unit = () + override def traverseTypeTree(tree: TypeOrBoundsTree)( + implicit ctx: Context): Unit = () + override def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = + () + override def traverseTypeCaseDef(tree: TypeCaseDef)(implicit ctx: Context): Unit = + () def getChildren(tree: Tree)(implicit ctx: Context): List[Tree] = { children = Nil @@ -37,7 +47,6 @@ class SemanticdbConsumer extends TastyConsumer { } object Traverser extends TreeTraverser { - val symbolsCache: HashMap[Symbol, String] = HashMap() implicit class TreeExtender(tree: Tree) { def isUserCreated: Boolean = { val children: List[Position] = @@ -86,21 +95,21 @@ class SemanticdbConsumer extends TastyConsumer { def resolveClass(symbol: ClassSymbol): Symbol = (symbol.companionClass, symbol.companionModule) match { - case (_, Some(module)) if symbol.flags.isObject => module - case (Some(c), _) => c - case _ => symbol + case (_, Some(module)) if symbol.flags.isObject => module + case (Some(c), _) => c + case _ => symbol } def disimbiguate(symbol_path: String, symbol: Symbol): String = { val symbolcl = resolveClass(symbol.owner.asClass) val methods = symbolcl.asClass.method(symbol.name) val (methods_count, method_pos) = - methods.foldLeft((0, -1))((x:Tuple2[Int, Int], m:Symbol) => { + methods.foldLeft((0, -1))((x: Tuple2[Int, Int], m: Symbol) => { if (m == symbol) - (x._1+1, x._1) - else - (x._1+1, x._2) - }) + (x._1 + 1, x._1) + else + (x._1 + 1, x._2) + }) val real_pos = methods_count - method_pos - 1 if (real_pos == 0) { @@ -262,9 +271,12 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(tree) } case PackageClause(_) => - addOccurenceTree(tree, - s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.name)) + if (!package_definitions(tree.symbol.name)) { + addOccurenceTree(tree, + s.SymbolOccurrence.Role.REFERENCE, + range(tree, tree.pos, tree.symbol.name)) + package_definitions += tree.symbol.name + } super.traverseTree(tree) case tree => diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 469ec765ebfe..82509096c5c6 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -2,42 +2,138 @@ package dotty.semanticdb import scala.tasty.Reflection import scala.tasty.file._ +import scala.collection.mutable.HashMap import org.junit.Test import org.junit.Assert._ import java.nio.file._ import scala.meta.internal.{semanticdb => s} import scala.collection.JavaConverters._ +import java.io.File +import scala.tasty.Reflection +import scala.tasty.file.TastyConsumer +import java.lang.reflect.InvocationTargetException + +class TastyInspecter extends TastyConsumer { + var source_path: Option[String] = None + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + object ChildTraverser extends TreeTraverser { + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = + tree match { + case IsClassDef(cdef) => { + cdef.symbol.annots.foreach { annot => + annot match { + case Term.Apply(Term.Select(Term.New(t), _, _), + List(Term.Literal(Constant.String(path)))) + if t.symbol.name == "SourceFile" => + // we found the path to a file. In this case, we do not need to + // continue traversing the tree + source_path = Some(path) + case x => super.traverseTree(tree) + } + true + } + } + case _ => { + if (source_path == None) + super.traverseTree(tree) + else + () + } + } + } + ChildTraverser.traverseTree(root)(reflect.rootContext) + } +} class Tests { // TODO: update scala-0.10 on version change (or resolve automatically) - final def tastyClassDirectory = "out/bootstrap/dotty-semanticdb/scala-0.11/test-classes" + final def tastyClassDirectory = + "out/bootstrap/dotty-semanticdb/scala-0.11/test-classes" val sourceroot = Paths.get("semanticdb", "input").toAbsolutePath val sourceDirectory = sourceroot.resolve("src/main/scala") val semanticdbClassDirectory = sourceroot.resolve("target/scala-2.12/classes") - val semanticdbLoader = new Semanticdbs.Loader(sourceroot, List(semanticdbClassDirectory)) + val semanticdbLoader = + new Semanticdbs.Loader(sourceroot, List(semanticdbClassDirectory)) + + val source_to_tasty = + getTastyFiles( + Paths.get("out", "bootstrap", "dotty-semanticdb/").toAbsolutePath) + /** Returns the SemanticDB for this Scala source file. */ def getScalacSemanticdb(scalaFile: Path): s.TextDocument = { semanticdbLoader.resolve(scalaFile).get } - /** TODO: Produce semanticdb from TASTy for this Scala source file. */ - def getTastySemanticdb(scalaFile: Path): s.TextDocument = { - val scalac = getScalacSemanticdb(scalaFile) - val pat = """(.*)\.scala""".r - val classpath = scalaFile.getParent().toString() - val modulename = sourceDirectory.relativize(scalaFile).getParent().getFileName().toString() + /** List all tasty files occuring in the folder f or one of its subfolders */ + def recursiveListFiles(f: File): Array[File] = { + val pattern = ".*test-classes/example.*\\.tasty".r + val files = f.listFiles + val folders = files.filter(_.isDirectory) + val tastyfiles = files.filter(_.toString match { + case pattern(x: _*) => true + case _ => false + }) + tastyfiles ++ folders.flatMap(recursiveListFiles) + } + + /** Returns a mapping from *.scala file to a list of tasty files. */ + def getTastyFiles(artifactsPath: Path): HashMap[String, List[Path]] = { + val source_to_tasty: HashMap[String, List[Path]] = HashMap() + val tastyfiles = recursiveListFiles(artifactsPath.toFile()) + recursiveListFiles(artifactsPath.toFile()).map(tasty_path => { + val (classpath, classname) = getClasspathClassname(tasty_path.toPath()) + // We add an exception here to avoid crashing if we encountered + // a bad tasty file + try { + val inspecter = new TastyInspecter + ConsumeTasty(classpath, classname :: Nil, inspecter) + inspecter.source_path.foreach( + source => + source_to_tasty += + (source -> (tasty_path + .toPath() :: source_to_tasty.getOrElse(source, Nil)))) + } catch { + case _: InvocationTargetException => println(tasty_path) + } + }) + source_to_tasty + } + + /** Infers a tuple (class path, class name) from a given path */ + def getClasspathClassname(file: Path): (String, String) = { + val pat = """(.*)\..*""".r + val classpath = file.getParent().getParent().toString() + val modulename = file.getParent().getFileName().toString() val sourcename = - scalaFile.toFile().getName().toString() match { - case pat(name) => name - case _ => "" + file.toFile().getName().toString() match { + case pat(name) => name + case _ => "" } - val sdbconsumer = new SemanticdbConsumer - val _ = ConsumeTasty(classpath, (modulename + "." + sourcename) :: Nil, sdbconsumer) - sdbconsumer.toSemanticdb(scalac.text) + return (classpath, modulename + "." + sourcename) + } + def getTastySemanticdb(scalaFile: Path): s.TextDocument = { + val scalac = getScalacSemanticdb(scalaFile) + + val tasty_files = source_to_tasty.getOrElse( + sourceDirectory.resolve(scalaFile).toString, + Nil) + + val tasty_classes = tasty_files.map(getClasspathClassname) + // If we have more than one classpath then something went wrong + if (tasty_classes.groupBy((a, _) => a).size != 1) { + scalac + } else { + val (classpaths, classnames) = tasty_classes.unzip + val sdbconsumer = new SemanticdbConsumer + + val _ = ConsumeTasty(classpaths.head, classnames, sdbconsumer) + sdbconsumer.toSemanticdb(scalac.text) + } } /** Fails the test if the s.TextDocument from tasty and semanticdb-scalac are not the same. */ @@ -61,9 +157,16 @@ class Tests { val diff = if (patch.getDeltas.isEmpty) "" else { - difflib.DiffUtils.generateUnifiedDiff( - "tasty", "scala2", obtainedLines, patch, 1 - ).asScala.mkString("\n") + difflib.DiffUtils + .generateUnifiedDiff( + "tasty", + "scala2", + obtainedLines, + patch, + 1 + ) + .asScala + .mkString("\n") } if (!diff.isEmpty) { fail("\n" + diff) From 4373c108e1ab59b0d247bed87e502eaaa36b0682 Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 22 Nov 2018 12:11:07 +0100 Subject: [PATCH 19/19] merged semanticdb/input/build.sbt with project/Build.scala --- build.sbt | 1 + project/Build.scala | 6 ++++++ semanticdb/test/dotty/semanticdb/Semanticdbs.scala | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 11804a2ccec1..2413a3402e2c 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,7 @@ val `dotty-language-server` = Build.`dotty-language-server` val `dotty-bench` = Build.`dotty-bench` val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped` val `dotty-semanticdb` = Build.`dotty-semanticdb` +val `dotty-semanticdb-input` = Build.`dotty-semanticdb-input` val `scala-library` = Build.`scala-library` val `scala-compiler` = Build.`scala-compiler` val `scala-reflect` = Build.`scala-reflect` diff --git a/project/Build.scala b/project/Build.scala index 07b7e30a9dc4..ef19c1f1bc92 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -919,6 +919,11 @@ object Build { lazy val `dotty-bench-bootstrapped` = project.in(file("bench")).asDottyBench(Bootstrapped) lazy val `dotty-semanticdb` = project.in(file("semanticdb")).asDottySemanticdb(Bootstrapped) + lazy val `dotty-semanticdb-input` = project.in(file("semanticdb/input")).settings( + scalaVersion := "2.12.7", + scalacOptions += "-Yrangepos", + addCompilerPlugin("org.scalameta" % "semanticdb-scalac" % "4.0.0" cross CrossVersion.full) + ) // Depend on dotty-library so that sbt projects using dotty automatically // depend on the dotty-library @@ -1317,6 +1322,7 @@ object Build { enablePlugins(JmhPlugin) def asDottySemanticdb(implicit mode: Mode): Project = project.withCommonSettings. + aggregate(`dotty-semanticdb-input`). dependsOn(dottyCompiler). settings(semanticdbSettings) diff --git a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala index 7c766c6e7380..d626558f2adf 100644 --- a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala +++ b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala @@ -7,7 +7,7 @@ import scala.collection.JavaConverters._ import dotty.tools.dotc.util.SourceFile object Semanticdbs { - + private val buildSubFolder = "semanticdb/input/" /** * Utility to load SemanticDB for Scala source files. * @@ -17,7 +17,7 @@ object Semanticdbs { * if you only care about reading SemanticDB files from a single project. */ class Loader(sourceroot: Path, classpath: List[Path]) { - private val META_INF = Paths.get("META-INF", "semanticdb") + private val META_INF = Paths.get("META-INF", "semanticdb").resolve(buildSubFolder) private val classLoader = new java.net.URLClassLoader(classpath.map(_.toUri.toURL).toArray) /** Returns a SemanticDB for a single Scala source file, if any. The path must be absolute. */ def resolve(scalaAbsolutePath: Path): Option[s.TextDocument] = { @@ -43,7 +43,7 @@ object Semanticdbs { scalaRelativePath: Path, semanticdbAbsolutePath: Path ): s.TextDocument = { - val reluri = scalaRelativePath.iterator.asScala.mkString("/") + val reluri = buildSubFolder + scalaRelativePath.iterator.asScala.mkString("/") val sdocs = parseTextDocuments(semanticdbAbsolutePath) sdocs.documents.find(_.uri == reluri) match { case None => throw new NoSuchElementException(reluri)