From 3e7af5cd67d279bc84a47e294151e29f5c5090e3 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 25 Nov 2018 18:33:52 +0100 Subject: [PATCH 01/42] Add local symbol handling --- .../tastyreflect/ImportSelectorOpsImpl.scala | 1 + .../dotc/tastyreflect/SymbolOpsImpl.scala | 7 + .../src/scala/tasty/reflect/SymbolOps.scala | 10 +- .../src/main/scala/example/Advanced.scala | 5 +- semanticdb/src/dotty/semanticdb/Scala.scala | 4 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 223 ++++++++++++++---- .../test/dotty/semanticdb/Semanticdbs.scala | 1 + semanticdb/test/dotty/semanticdb/Tests.scala | 3 + 8 files changed, 204 insertions(+), 50 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ImportSelectorOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ImportSelectorOpsImpl.scala index fc71e87f682e..8300b213d93f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ImportSelectorOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ImportSelectorOpsImpl.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc.tastyreflect import dotty.tools.dotc.ast.{Trees, untpd} import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Decorators._ trait ImportSelectorOpsImpl extends scala.tasty.reflect.ImportSelectorOps with CoreImpl { diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index b096c7d83d5a..2fe4b4281060 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -30,6 +30,13 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def owner(implicit ctx: Context): Symbol = symbol.owner + def isLocalDummy(implicit ctx: Context): Boolean = symbol.isLocalDummy + def isRefinementClass(implicit ctx: Context): Boolean = symbol.isRefinementClass + def isAliasType(implicit ctx: Context): Boolean = symbol.isAliasType + def isAnonymousClass(implicit ctx: Context): Boolean = symbol.isAnonymousClass + def isAnonymousFunction(implicit ctx: Context): Boolean = symbol.isAnonymousFunction + def isAbstractType(implicit ctx: Context): Boolean = symbol.isAbstractType + 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 2f1af7b87bea..647be0114192 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -14,6 +14,13 @@ trait SymbolOps extends Core { /** Flags of this symbol */ def flags(implicit ctx: Context): Flags + def isLocalDummy(implicit ctx: Context): Boolean + def isRefinementClass(implicit ctx: Context): Boolean + def isAliasType(implicit ctx: Context): Boolean + def isAnonymousClass(implicit ctx: Context): Boolean + def isAnonymousFunction(implicit ctx: Context): Boolean + def isAbstractType(implicit ctx: Context): Boolean + /** This symbol is private within the resulting type. */ def privateWithin(implicit ctx: Context): Option[Type] @@ -26,6 +33,7 @@ trait SymbolOps extends Core { /** The full name of this symbol up to the root package. */ def fullName(implicit ctx: Context): String + /** The position of this symbol */ def pos(implicit ctx: Context): Position def localContext(implicit ctx: Context): Context @@ -170,7 +178,7 @@ trait SymbolOps extends Core { } trait BindSymbolAPI { - /** Bind pattern of this defintion. */ + /** Bind pattern of this definition. */ def tree(implicit ctx: Context): Bind } implicit def BindSymbolDeco(symbol: BindSymbol): BindSymbolAPI diff --git a/semanticdb/input/src/main/scala/example/Advanced.scala b/semanticdb/input/src/main/scala/example/Advanced.scala index fdee5eccad19..4f3e688b7d48 100644 --- a/semanticdb/input/src/main/scala/example/Advanced.scala +++ b/semanticdb/input/src/main/scala/example/Advanced.scala @@ -1,7 +1,6 @@ package example -import scala.language.existentials -import scala.language.higherKinds +import scala.language.{existentials, higherKinds=>h} import scala.language.reflectiveCalls class AdvC[T] { @@ -36,4 +35,4 @@ object AdvTest { () } } -} +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/Scala.scala b/semanticdb/src/dotty/semanticdb/Scala.scala index 94f2d76b7bc0..2d752e8b3c0d 100644 --- a/semanticdb/src/dotty/semanticdb/Scala.scala +++ b/semanticdb/src/dotty/semanticdb/Scala.scala @@ -173,7 +173,9 @@ object Scala { val Constructor: TermName = TermName("") private[semanticdb] def encode(value: String): String = { - if (value == "") { + if (value == "scalaShadowing") { + "scala" + } else if (value == "") { "``" } else { val (start, parts) = (value.head, value.tail) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index ae02f485bf1a..fe8c4b0efc93 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -19,7 +19,8 @@ class SemanticdbConsumer extends TastyConsumer { def toSemanticdb(text: String): s.TextDocument = { s.TextDocument(text = text, occurrences = occurrences) } - val package_definitions: Set[String] = Set() + val package_definitions: Set[Tuple2[String, Int]] = Set() + var local_offset: Int = 0 final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -36,7 +37,8 @@ class SemanticdbConsumer extends TastyConsumer { implicit ctx: Context): Unit = () override def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = () - override def traverseTypeCaseDef(tree: TypeCaseDef)(implicit ctx: Context): Unit = + override def traverseTypeCaseDef(tree: TypeCaseDef)( + implicit ctx: Context): Unit = () def getChildren(tree: Tree)(implicit ctx: Context): List[Tree] = { @@ -73,6 +75,8 @@ class SemanticdbConsumer extends TastyConsumer { case _ => false } + def isTerm: Boolean = !symbol.isType + def isMethod: Boolean = symbol match { case IsDefSymbol(_) => true case _ => false @@ -83,6 +87,8 @@ class SemanticdbConsumer extends TastyConsumer { case _ => false } + def isParameter: Boolean = symbol.flags.is(Flags.Param) + def isObject: Boolean = symbol.flags.is(Flags.Object) def isTrait: Boolean = symbol.flags.is(Flags.Trait) @@ -91,8 +97,45 @@ class SemanticdbConsumer extends TastyConsumer { // TODO : implement it def isJavaClass: Boolean = false - } + def isSelfParameter: Boolean = + symbol != NoSymbol && symbol.owner == symbol + + def isSemanticdbLocal: Boolean = { + def definitelyGlobal = symbol.isPackage + def definitelyLocal = + symbol == NoSymbol || + (symbol.owner.isTerm && !symbol.isParameter) || + ((symbol.owner.isAliasType || symbol.owner.isAbstractType) && !symbol.isParameter) || + symbol.isSelfParameter || + symbol.isLocalDummy || + symbol.isRefinementClass || + symbol.isAnonymousClass || + symbol.isAnonymousFunction /*|| + symbol.isExistential*/ + def ownerLocal = symbol.owner.isSemanticdbLocal + !definitelyGlobal && (definitelyLocal || ownerLocal) + } + + def isUseless: Boolean = { + symbol == NoSymbol || + symbol.isAnonymousClass || /* + sym.isSyntheticConstructor || + sym.isStaticConstructor || + sym.isLocalChild || + sym.isSyntheticValueClassCompanion || + sym.isUselessField || + sym.isSyntheticCaseAccessor ||*/ + symbol.isRefinementClass /* || + sym.isSyntheticJavaModule*/ + } + def isUseful: Boolean = !symbol.isUseless + def isUselessOccurrence: Boolean = { + symbol.isUseless /*&& + !symbol.isSyntheticJavaModule // references to static Java inner classes should have occurrences +*/ + } + } def resolveClass(symbol: ClassSymbol): Symbol = (symbol.companionClass, symbol.companionModule) match { case (_, Some(module)) if symbol.flags.is(Flags.Object) => module @@ -101,21 +144,31 @@ class SemanticdbConsumer extends TastyConsumer { } 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 + ")" + try { + val symbolcl = resolveClass(symbol.owner.asClass) + symbolcl match { + case IsClassSymbol(classsymbol) => { + val methods = classsymbol.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 + ")" + } + } + case _ => + "()" + } + } catch { + case _ => "()" } } @@ -134,14 +187,19 @@ class SemanticdbConsumer extends TastyConsumer { if (symbol.isPackage) { d.Package(symbol.name) } else if (symbol.isObject) { - d.Term(resolveClass(symbol.asClass).name) + symbol match { + case IsClassSymbol(classsymbol) => + d.Term(resolveClass(classsymbol).name) + case _ => + d.Term(symbol.name) + } } else if (symbol.isMethod) { d.Method(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.isValueParameter) { + d.Parameter(symbol.name) } else if (symbol.isType || symbol.isTrait) { d.Type(symbol.name) } else { @@ -158,8 +216,22 @@ class SemanticdbConsumer extends TastyConsumer { def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - val symbol_path = iterateParent(symbol) - if (symbol_path == "") return + val symbol_path = + if (symbol.isSemanticdbLocal) { + println("LOCAL: ", symbol) + if (symbolsCache.contains(symbol)) { + symbolsCache(symbol) + } else { + var localsymbol = Symbols.Local(local_offset.toString) + local_offset += 1 + symbolsCache += (symbol -> localsymbol) + localsymbol + } + } else { + iterateParent(symbol) + } + + if (symbol_path == "" || symbol.isUselessOccurrence) return occurrences = occurrences :+ @@ -168,16 +240,16 @@ 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 = { + println(tree.symbol) if (tree.isUserCreated || force_add) { addOccurence(tree.symbol, type_symbol, range) - } + } } def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, @@ -186,6 +258,20 @@ class SemanticdbConsumer extends TastyConsumer { addOccurence(typetree.symbol, type_symbol, range) } } + def addOccurenceId(parent_path: String, id: Id): Unit = { + val symbol_path = Symbols.Global(parent_path, d.Term(id.name)) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some( + s.Range(id.pos.startLine, + id.pos.startColumn, + id.pos.startLine, + id.pos.endColumn)), + symbol_path, + s.SymbolOccurrence.Role.REFERENCE + ) + } def range(tree: Tree, pos: Position, name: String): s.Range = { val offset = tree match { @@ -208,8 +294,8 @@ class SemanticdbConsumer extends TastyConsumer { 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, + return s.Range(exclude.endLine, + exclude.endColumn + 1, range.endLine, range.endColumn) } @@ -224,9 +310,51 @@ class SemanticdbConsumer extends TastyConsumer { range(tree, typetree.pos, typetree.symbol.name)) } + def getImportPath(path_term: Term): String = { + path_term match { + case Term.Select(qualifier, selected) => { + getImportPath(qualifier) + val range = rangeExclude(path_term.pos, qualifier.pos) + addOccurenceTree(path_term, + s.SymbolOccurrence.Role.REFERENCE, + range) + iterateParent(path_term.symbol) + } + case Term.Ident(x) => { + val range_x = range(path_term, path_term.pos, path_term.symbol.name) + addOccurenceTree(path_term, + s.SymbolOccurrence.Role.REFERENCE, + range_x) + iterateParent(path_term.symbol) + } + } + } + + def getImportSelectors(parent_path: String, + selectors: List[ImportSelector]): Unit = { + selectors.foreach(selector => + selector match { + case SimpleSelector(id) => { + addOccurenceId(parent_path, id) + } + case RenameSelector(id, _) => { + addOccurenceId(parent_path, id) + } + case OmitSelector(id) => { + addOccurenceId(parent_path, id) + } + }) + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) tree match { + case Import(path, selectors) => + val key = (tree.symbol.name, tree.pos.start) + if (!package_definitions(key)) { + package_definitions += key + getImportSelectors(getImportPath(path), selectors) + } case IsDefinition(body) => { tree match { case DefDef(name, _, _, typetree, _) => @@ -236,27 +364,31 @@ class SemanticdbConsumer extends TastyConsumer { case _ => () } - 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) + println(tree.symbol.name) + if (tree.symbol.name != "") { + val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) + /*if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.isObject) { + 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) + println(range.startCharacter, range.endCharacter) addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTree(tree) } @@ -271,11 +403,12 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(tree) } case PackageClause(_) => - if (!package_definitions(tree.symbol.name)) { + val key = (tree.symbol.name, tree.pos.start) + if (!package_definitions(key)) { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range(tree, tree.pos, tree.symbol.name)) - package_definitions += tree.symbol.name + package_definitions += key } super.traverseTree(tree) @@ -285,7 +418,7 @@ class SemanticdbConsumer extends TastyConsumer { } } - + println(root) Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala index c237a7db4deb..225d375bd5b2 100644 --- a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala +++ b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala @@ -87,6 +87,7 @@ object Semanticdbs { occurrences.foreach { occ => val range = occ.range.get val end = sourceFile.lineToOffset(range.endLine) + range.endCharacter + //println(occ) sb.append(doc.text.substring(offset, end)) sb.append(" /* ") .append(occ.symbol) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index ab38ff7308ac..628e6cc0ebba 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -141,6 +141,7 @@ class Tests { val path = sourceDirectory.resolve(filename) val scalac = getScalacSemanticdb(path) val tasty = getTastySemanticdb(path) + println(tasty.occurrences) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) assertNoDiff(obtained, expected) @@ -175,6 +176,8 @@ class Tests { @Test def testExample(): Unit = checkFile("example/Example.scala") + @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") + //@Test def testLocals(): Unit = checkFile("example/Locals.scala") // TODO: add more tests def testOutput(className: String, expected: String): Unit = { From c944b5e2b997e5555898ea6fcc38b5b059f55b01 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 26 Nov 2018 12:25:58 +0100 Subject: [PATCH 02/42] Refactore symbol creation for type, add tests --- .../tastyreflect/TypeOrBoundsOpsImpl.scala | 2 + .../scala/tasty/reflect/TypeOrBoundsOps.scala | 1 + .../src/main/scala/example/Advanced.scala | 2 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 102 ++++++++++++++---- semanticdb/test/dotty/semanticdb/Tests.scala | 27 ++++- 5 files changed, 111 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index 780040bbfae1..f375733f5e00 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -21,6 +21,8 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI def classSymbol(implicit ctx: Context): Option[ClassSymbol] = if (tpe.classSymbol.exists) Some(tpe.classSymbol.asClass) else None + + def typeSymbol(implicit ctx: Context): Symbol = tpe.typeSymbol } def ConstantTypeDeco(x: ConstantType): Type.ConstantTypeAPI = new Type.ConstantTypeAPI { diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 2e5a90ddf619..1063d4345e25 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -54,6 +54,7 @@ trait TypeOrBoundsOps extends Core { def <:<(other: Type)(implicit ctx: Context): Boolean def widen(implicit ctx: Context): Type def classSymbol(implicit ctx: Context): Option[ClassSymbol] + def typeSymbol(implicit ctx: Context): Symbol } val IsType: IsTypeModule diff --git a/semanticdb/input/src/main/scala/example/Advanced.scala b/semanticdb/input/src/main/scala/example/Advanced.scala index 4f3e688b7d48..b9aa9c0c8bbc 100644 --- a/semanticdb/input/src/main/scala/example/Advanced.scala +++ b/semanticdb/input/src/main/scala/example/Advanced.scala @@ -35,4 +35,4 @@ object AdvTest { () } } -} \ No newline at end of file +} diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index fe8c4b0efc93..bf3d1d0a6e6b 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -133,7 +133,7 @@ class SemanticdbConsumer extends TastyConsumer { def isUselessOccurrence: Boolean = { symbol.isUseless /*&& !symbol.isSyntheticJavaModule // references to static Java inner classes should have occurrences -*/ + */ } } def resolveClass(symbol: ClassSymbol): Symbol = @@ -218,7 +218,6 @@ class SemanticdbConsumer extends TastyConsumer { range: s.Range): Unit = { val symbol_path = if (symbol.isSemanticdbLocal) { - println("LOCAL: ", symbol) if (symbolsCache.contains(symbol)) { symbolsCache(symbol) } else { @@ -246,10 +245,9 @@ class SemanticdbConsumer extends TastyConsumer { type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { - println(tree.symbol) if (tree.isUserCreated || force_add) { addOccurence(tree.symbol, type_symbol, range) - } + } } def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, @@ -275,8 +273,9 @@ class SemanticdbConsumer extends TastyConsumer { 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 IsClassDef(tree) if tree.symbol.flags.is(Flags.Object) => -1 + case _ => 0 } val range_end_column = @@ -346,6 +345,50 @@ class SemanticdbConsumer extends TastyConsumer { }) } + override def traverseTypeTree(tree: TypeOrBoundsTree)( + implicit ctx: Context): Unit = { + tree match { + case TypeTree.Ident(_) => { + tree match { + case IsTypeTree(typetree) => { + addOccurenceTypeTree(typetree, + s.SymbolOccurrence.Role.REFERENCE, + s.Range(typetree.pos.startLine, + typetree.pos.startColumn, + typetree.pos.startLine, + typetree.pos.endColumn)) + } + case _ => + super.traverseTypeTree(tree) + } + } + case _ => + super.traverseTypeTree(tree) + } + } + + /*override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { + println("CASE", tree) + tree match { + case Pattern.Value(term) => { + term match { + + case Term.Ident(name) => { + // To avoid adding the identifier of the package symbol + if (term.symbol.owner.name != "") { + addOccurenceTree(term, + s.SymbolOccurrence.Role.REFERENCE, + range(term, term.pos, term.symbol.name)) + } + super.traversePattern(tree) + } + } + } + case _ => + super.traversePattern(tree) + } + }*/ + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) tree match { @@ -355,16 +398,39 @@ class SemanticdbConsumer extends TastyConsumer { package_definitions += key getImportSelectors(getImportPath(path), selectors) } - case IsDefinition(body) => { - tree match { - case DefDef(name, _, _, typetree, _) => - typetreeSymbol(tree, typetree) - case ValDef(_, typetree, _) => - typetreeSymbol(tree, typetree) - case _ => () + case IsDefinition(cdef) => { + + if (cdef.symbol.flags.is(Flags.Protected)) { + cdef.symbol.protectedWithin match { + case Some(within) => { + val startColumn = cdef.pos.startColumn + "protected[".length + addOccurence( + within.typeSymbol, + s.SymbolOccurrence.Role.REFERENCE, + s.Range(cdef.pos.startLine, + startColumn, + cdef.pos.startLine, + startColumn + within.typeSymbol.name.length) + ) + } + case _ => + } + } else { + cdef.symbol.privateWithin match { + case Some(within) => { + val startColumn = cdef.pos.startColumn + "private[".length + addOccurence( + within.typeSymbol, + s.SymbolOccurrence.Role.REFERENCE, + s.Range(cdef.pos.startLine, + startColumn, + cdef.pos.startLine, + startColumn + within.typeSymbol.name.length) + ) + } + case _ => + } } - - println(tree.symbol.name) if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) /*if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.isObject) { @@ -383,17 +449,16 @@ class SemanticdbConsumer extends TastyConsumer { range_symbol) } } - super.traverseTree(body) + super.traverseTree(cdef) } case Term.Select(qualifier, _) => { val range = rangeExclude(tree.pos, qualifier.pos) - println(range.startCharacter, range.endCharacter) addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTree(tree) } - case Term.Ident(_) => { + case Term.Ident(name) => { // To avoid adding the identifier of the package symbol if (tree.symbol.owner.name != "") { addOccurenceTree(tree, @@ -418,7 +483,6 @@ class SemanticdbConsumer extends TastyConsumer { } } - println(root) Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 628e6cc0ebba..870ec4e051d6 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -141,7 +141,6 @@ class Tests { val path = sourceDirectory.resolve(filename) val scalac = getScalacSemanticdb(path) val tasty = getTastySemanticdb(path) - println(tasty.occurrences) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) assertNoDiff(obtained, expected) @@ -175,10 +174,32 @@ class Tests { } - @Test def testExample(): Unit = checkFile("example/Example.scala") + //@Test def testAccess(): Unit = checkFile("example/Access.scala") @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") + //WIP(assert) @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + //WIP(assert because of case) @Test def testClasses(): Unit = checkFile("example/Classes.scala") + //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") + //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") + //@Test def testExample(): Unit = checkFile("example/Example.scala") + //WIP @Test def testExample2(): Unit = checkFile("example/Example2.scala") + //@Test def testExclude(): Unit = checkFile("example/Exclude.scala") + //WIP(assert) @Test def testFlags(): Unit = checkFile("example/Flags.scala") + //@Test def testImports(): Unit = checkFile("example/Imports.scala") + //@Test def testIssue1749(): Unit = checkFile("example/Issue1749.scala") + //@Test def testLocalFile(): Unit = checkFile("example/local-file.scala") //@Test def testLocals(): Unit = checkFile("example/Locals.scala") - // TODO: add more tests + //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") + //WIP @Test def testMethods(): Unit = checkFile("example/Methods.scala") + //WIP @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") + //@Test def testObjects(): Unit = checkFile("example/Objects.scala") + //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") + //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") + //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + //WIP(assert nodenotation owner) @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") + //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") + //WIP(assert) @Test def testTypes(): Unit = checkFile("example/Types.scala") + //WIP @Test def testVals(): Unit = checkFile("example/Vals.scala") + def testOutput(className: String, expected: String): Unit = { val out = new StringBuilder From 77c141ef64d16acb848df88c3125ae000b442c56 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 27 Nov 2018 00:51:10 +0100 Subject: [PATCH 03/42] Port logic from semanticdb --- .../dotc/tastyreflect/FlagsOpsImpl.scala | 6 + .../dotc/tastyreflect/SymbolOpsImpl.scala | 6 + .../src/scala/tasty/reflect/FlagsOps.scala | 12 ++ .../src/scala/tasty/reflect/SymbolOps.scala | 2 + .../src/main/scala/example/Anonymous.scala | 10 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 133 ++++++++++++++---- semanticdb/test/dotty/semanticdb/Tests.scala | 6 +- .../pos-with-compiler/tasty/definitions.scala | 2 +- 8 files changed, 145 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala index d71935e79a50..b16094e9d649 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala @@ -43,6 +43,12 @@ trait FlagsOpsImpl extends scala.tasty.reflect.FlagsOps with CoreImpl { def StableRealizable: Flags = core.Flags.StableRealizable def Param: Flags = core.Flags.Param def ParamAccessor: Flags = core.Flags.ParamAccessor + def JavaDefined: Flags = core.Flags.JavaDefined + def Enum: Flags = core.Flags.Enum + def ModuleClass: Flags = core.Flags.ModuleClass + def PrivateLocal: Flags = core.Flags.PrivateLocal + def Package: Flags = core.Flags.Package + def ImplClass: Flags = core.Flags.ImplClass } } diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index 2fe4b4281060..1e2600a3d86c 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -36,6 +36,7 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def isAnonymousClass(implicit ctx: Context): Boolean = symbol.isAnonymousClass def isAnonymousFunction(implicit ctx: Context): Boolean = symbol.isAnonymousFunction def isAbstractType(implicit ctx: Context): Boolean = symbol.isAbstractType + def isClassConstructor(implicit ctx: Context): Boolean = symbol.isClassConstructor def localContext(implicit ctx: Context): Context = { if (symbol.exists) ctx.withOwner(symbol) @@ -174,6 +175,11 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { if (sym.exists) Some(sym.asTerm) else None } + def moduleClass(implicit ctx: Context): Option[Symbol] = { + val sym = symbol.moduleClass + if (sym.exists) Some(sym.asTerm) else None + } + private def isField(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm && !sym.is(Flags.Method) } diff --git a/library/src/scala/tasty/reflect/FlagsOps.scala b/library/src/scala/tasty/reflect/FlagsOps.scala index eafef4cf3d01..0f87394fbed6 100644 --- a/library/src/scala/tasty/reflect/FlagsOps.scala +++ b/library/src/scala/tasty/reflect/FlagsOps.scala @@ -101,6 +101,18 @@ trait FlagsOps extends Core { /** Is this symbol a parameter accessor */ def ParamAccessor: Flags + + def JavaDefined: Flags + + def Enum: Flags + + def ModuleClass: Flags + + def PrivateLocal: Flags + + def Package: Flags + + def ImplClass: Flags } } diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index 647be0114192..6d3b2439cbf4 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -20,6 +20,7 @@ trait SymbolOps extends Core { def isAnonymousClass(implicit ctx: Context): Boolean def isAnonymousFunction(implicit ctx: Context): Boolean def isAbstractType(implicit ctx: Context): Boolean + def isClassConstructor(implicit ctx: Context): Boolean /** This symbol is private within the resulting type. */ def privateWithin(implicit ctx: Context): Option[Type] @@ -119,6 +120,7 @@ trait SymbolOps extends Core { /** The symbol of the companion module */ def companionModule(implicit ctx: Context): Option[ValSymbol] + def moduleClass(implicit ctx: Context): Option[Symbol] } implicit def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI diff --git a/semanticdb/input/src/main/scala/example/Anonymous.scala b/semanticdb/input/src/main/scala/example/Anonymous.scala index aae065fa7ae3..83317b3c06db 100644 --- a/semanticdb/input/src/main/scala/example/Anonymous.scala +++ b/semanticdb/input/src/main/scala/example/Anonymous.scala @@ -4,15 +4,15 @@ import scala.language.higherKinds class Anonymous { this: Anonymous => - def m1[T[_], B] = ??? + /*def m1[T[_], B] = ??? def m2: Map[_, List[_]] = ??? locally { ??? match { case _: List[_] => } - } + }*/ locally { val x: Int => Int = _ => ??? } - + /* trait Foo - var x = new Foo {} -} + var x = new Foo {}*/ +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index bf3d1d0a6e6b..99398e866892 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -4,6 +4,7 @@ import scala.tasty.Reflection import scala.tasty.file.TastyConsumer import dotty.tools.dotc.tastyreflect +import dotty.tools.dotc.core.StdNames._ import scala.collection.mutable.HashMap import scala.collection.mutable.Set import scala.meta.internal.{semanticdb => s} @@ -65,11 +66,13 @@ class SemanticdbConsumer extends TastyConsumer { } implicit class SymbolExtender(symbol: Symbol) { - def isTypeParameter: Boolean = symbol match { - case IsTypeSymbol(_) => symbol.flags.is(Flags.Param) - case _ => false + def isClass: Boolean = symbol match { + case IsClassSymbol(_) => true + case _ => false } + def isTypeParameter: Boolean = symbol.flags.is(Flags.Param) && symbol.isType + def isType: Boolean = symbol match { case IsTypeSymbol(_) => true case _ => false @@ -95,13 +98,12 @@ class SemanticdbConsumer extends TastyConsumer { def isValueParameter: Boolean = symbol.flags.is(Flags.Param) - // TODO : implement it - def isJavaClass: Boolean = false + def isJavaClass: Boolean = symbol.isClass && symbol.flags.is(Flags.JavaDefined) - def isSelfParameter: Boolean = + def isSelfParameter(implicit ctx: Context): Boolean = symbol != NoSymbol && symbol.owner == symbol - def isSemanticdbLocal: Boolean = { + def isSemanticdbLocal(implicit ctx: Context): Boolean = { def definitelyGlobal = symbol.isPackage def definitelyLocal = symbol == NoSymbol || @@ -117,25 +119,107 @@ class SemanticdbConsumer extends TastyConsumer { !definitelyGlobal && (definitelyLocal || ownerLocal) } - def isUseless: Boolean = { + def isConstructor(implicit ctx: Context): Boolean = + symbol.name == "" + + def isSyntheticConstructor(implicit ctx: Context): Boolean = { + val isModuleConstructor = symbol.isConstructor && symbol.owner.isClass + val isTraitConstructor = symbol.isConstructor && symbol.owner.isTrait + val isInterfaceConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.isTrait + val isEnumConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.flags.is(Flags.Enum) + /*val isStaticConstructor = symbol.name == g.TermName("")*/ + //val isClassfileAnnotationConstructor = symbol.owner.isClassfileAnnotation + isModuleConstructor || isTraitConstructor || isInterfaceConstructor || + isEnumConstructor /*|| isStaticConstructor || isClassfileAnnotationConstructor*/ + } + def isLocalChild(implicit ctx: Context): Boolean = + symbol.name == tpnme.LOCAL_CHILD.toString + + def isSyntheticValueClassCompanion(implicit ctx: Context): Boolean = { + if (symbol.isClass) { + if (symbol.flags.is(Flags.Object)) { + symbol.asClass.moduleClass.fold(false)(c => + c.isSyntheticValueClassCompanion) + } else { + symbol.flags.is(Flags.ModuleClass) && + symbol.flags.is(Flags.Synthetic) && + symbol.asClass.methods.length == 0 + } + } else { + false + } + } + def isValMethod(implicit ctx: Context): Boolean = { + symbol.isMethod && { + (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.Stable) ) || + (symbol.isUsefulField && !symbol.flags.is(Flags.Mutable) ) + } + } + def isScalacField(implicit ctx: Context): Boolean = { + val isFieldForPrivateThis = symbol.flags.is(Flags.PrivateLocal) && symbol.isTerm && !symbol.isMethod && !symbol.isObject + val isFieldForOther = false //symbol.name.endsWith(g.nme.LOCAL_SUFFIX_STRING) + val isJavaDefined = symbol.flags.is(Flags.JavaDefined) + (isFieldForPrivateThis || isFieldForOther) && !isJavaDefined + } + def isUselessField(implicit ctx: Context): Boolean = { + symbol.isScalacField && false /*symbol.getterIn(symbol.owner) != g.NoSymbol*/ + } + def isUsefulField(implicit ctx: Context): Boolean = { + symbol.isScalacField && !symbol.isUselessField + } + def isSyntheticCaseAccessor(implicit ctx: Context): Boolean = { + symbol.flags.is(Flags.CaseAcessor) && symbol.name.contains("$") + } + def isSyntheticJavaModule(implicit ctx: Context): Boolean = { + !symbol.flags.is(Flags.Package) && symbol.flags.is(Flags.JavaDefined) && symbol.flags.is(Flags.Object) + } + def isAnonymousClassConstructor(implicit ctx: Context): Boolean = { + symbol.isConstructor && symbol.owner.isAnonymousClass + } + def isSyntheticAbstractType(implicit ctx: Context): Boolean = { + symbol.flags.is(Flags.Synthetic) && symbol.isAbstractType // these are hardlinked to TypeOps + } + def isEtaExpandedParameter(implicit ctx: Context): Boolean = { + // Term.Placeholder occurrences are not persisted so we don't persist their symbol information. + // We might want to revisit this decision https://github.com/scalameta/scalameta/issues/1657 + symbol.isParameter && + symbol.name.startsWith("x$") && + symbol.owner.isAnonymousFunction + } + def isAnonymousSelfParameter(implicit ctx: Context): Boolean = { + symbol.isSelfParameter && { + symbol.name == tpnme.this_.toString || // hardlinked in ClassSignature.self + symbol.name.startsWith("x$") // wildcards can't be referenced: class A { _: B => } + } + } + def isStaticMember(implicit ctx: Context): Boolean = + (symbol == NoSymbol) && + (symbol.flags.is(Flags.Static) || symbol.owner.flags.is(Flags.ImplClass) || + /*symbol.annots.find(_ == ctx.definitions.ScalaStaticAnnot)*/ false) + + def isStaticConstructor(implicit ctx: Context): Boolean = { + (symbol.isStaticMember && symbol.isClassConstructor) || (symbol.name == tpnme.STATIC_CONSTRUCTOR.toString) + } + def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || - symbol.isAnonymousClass || /* - sym.isSyntheticConstructor || - sym.isStaticConstructor || - sym.isLocalChild || - sym.isSyntheticValueClassCompanion || - sym.isUselessField || - sym.isSyntheticCaseAccessor ||*/ - symbol.isRefinementClass /* || - sym.isSyntheticJavaModule*/ - } - def isUseful: Boolean = !symbol.isUseless - def isUselessOccurrence: Boolean = { - symbol.isUseless /*&& - !symbol.isSyntheticJavaModule // references to static Java inner classes should have occurrences - */ + symbol.isAnonymousClass || + symbol.isAnonymousFunction || + symbol.isSyntheticConstructor || + symbol.isStaticConstructor || + symbol.isLocalChild || + symbol.isSyntheticValueClassCompanion || + symbol.isUselessField || + symbol.isSyntheticCaseAccessor || + symbol.isRefinementClass || + symbol.isSyntheticJavaModule + } + def isUseful(implicit ctx: Context): Boolean = !symbol.isUseless + def isUselessOccurrence(implicit ctx: Context): Boolean = { + symbol.isUseless && + !symbol.isSyntheticJavaModule // references to static Java inner classes should have occurrences } } + def resolveClass(symbol: ClassSymbol): Symbol = (symbol.companionClass, symbol.companionModule) match { case (_, Some(module)) if symbol.flags.is(Flags.Object) => module @@ -221,6 +305,7 @@ class SemanticdbConsumer extends TastyConsumer { if (symbolsCache.contains(symbol)) { symbolsCache(symbol) } else { + println("local: ", symbol, local_offset) var localsymbol = Symbols.Local(local_offset.toString) local_offset += 1 symbolsCache += (symbol -> localsymbol) @@ -433,7 +518,7 @@ class SemanticdbConsumer extends TastyConsumer { } if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - /*if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.isObject) { + /*if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.is(Flags.Object) ) { val range_symbol2 = s.Range(range_symbol.startLine, range_symbol.startCharacter - 4, range_symbol.endLine, diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 870ec4e051d6..e2e2ca49a102 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -141,6 +141,7 @@ class Tests { val path = sourceDirectory.resolve(filename) val scalac = getScalacSemanticdb(path) val tasty = getTastySemanticdb(path) + println(tasty) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) assertNoDiff(obtained, expected) @@ -175,8 +176,8 @@ class Tests { //@Test def testAccess(): Unit = checkFile("example/Access.scala") - @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") - //WIP(assert) @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + //@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") + @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") //WIP(assert because of case) @Test def testClasses(): Unit = checkFile("example/Classes.scala") //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") @@ -199,6 +200,7 @@ class Tests { //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") //WIP(assert) @Test def testTypes(): Unit = checkFile("example/Types.scala") //WIP @Test def testVals(): Unit = checkFile("example/Vals.scala") + @Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") def testOutput(className: String, expected: String): Unit = { diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index 40363f4e8eeb..f5358aee3d4a 100644 --- a/tests/pos-with-compiler/tasty/definitions.scala +++ b/tests/pos-with-compiler/tasty/definitions.scala @@ -246,7 +246,7 @@ object definitions { def isMutable: Boolean // when used on a ValDef: a var def isLabel: Boolean // method generated as a label def isFieldAccessor: Boolean // a getter or setter - def isCaseAcessor: Boolean // getter for class parameter + def isCaseAccessor: Boolean // getter for class parameter def isCovariant: Boolean // type parameter marked “+” def isContravariant: Boolean // type parameter marked “-” def isScala2X: Boolean // Imported from Scala2.x From 863544f6e99027594591ec523ec19011b83c828e Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 27 Nov 2018 01:04:15 +0100 Subject: [PATCH 04/42] fix wildcard issue --- semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 99398e866892..35ec8b44505a 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -200,8 +200,15 @@ class SemanticdbConsumer extends TastyConsumer { def isStaticConstructor(implicit ctx: Context): Boolean = { (symbol.isStaticMember && symbol.isClassConstructor) || (symbol.name == tpnme.STATIC_CONSTRUCTOR.toString) } + + def isWildCard(implicit ctx: Context): Boolean = { + symbol.name.startsWith(tpnme.WILDCARD.toString) && + symbol.name != tpnme.THIS.toString + } + def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || + symbol.isWildCard || symbol.isAnonymousClass || symbol.isAnonymousFunction || symbol.isSyntheticConstructor || @@ -305,13 +312,13 @@ class SemanticdbConsumer extends TastyConsumer { if (symbolsCache.contains(symbol)) { symbolsCache(symbol) } else { - println("local: ", symbol, local_offset) var localsymbol = Symbols.Local(local_offset.toString) local_offset += 1 symbolsCache += (symbol -> localsymbol) localsymbol } } else { + println("golbal: ", symbol) iterateParent(symbol) } From eb580a3dc68e00618c76bf42b4ac44b143f34f28 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 28 Nov 2018 16:40:05 +0100 Subject: [PATCH 05/42] fix package path printing --- .../dotty/semanticdb/SemanticdbConsumer.scala | 36 +++++++++++++------ semanticdb/test/dotty/semanticdb/Tests.scala | 14 ++++---- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 35ec8b44505a..0f741b054a30 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -318,10 +318,22 @@ class SemanticdbConsumer extends TastyConsumer { localsymbol } } else { - println("golbal: ", symbol) + println("global: ", symbol) iterateParent(symbol) } - + /*println(symbol, symbol.isUselessOccurrence) + + println(symbol, "isWildCard", symbol.isWildCard) + println(symbol, "isAnonymousCl", symbol.isAnonymousClass) + println(symbol, "isAnonymousFun", symbol.isAnonymousFunction) + println(symbol, "isSyntheticCon", symbol.isSyntheticConstructor) + println(symbol, "isStaticConstr", symbol.isStaticConstructor) + println(symbol, "isLocalChil", symbol.isLocalChild) + println(symbol, "isSyntheticVal", symbol.isSyntheticValueClassCompanion) + println(symbol, "isUselessFie", symbol.isUselessField) + println(symbol, "isSyntheticCas", symbol.isSyntheticCaseAccessor) + println(symbol, "isRefinementCl", symbol.isRefinementClass) + println(symbol, "isSyntheticJav", symbol.isSyntheticJavaModule)*/ if (symbol_path == "" || symbol.isUselessOccurrence) return occurrences = @@ -482,7 +494,9 @@ class SemanticdbConsumer extends TastyConsumer { }*/ override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - //println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) + println("\n") + println(tree) + println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) tree match { case Import(path, selectors) => val key = (tree.symbol.name, tree.pos.start) @@ -525,7 +539,8 @@ class SemanticdbConsumer extends TastyConsumer { } if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - /*if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.is(Flags.Object) ) { + if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.is(Flags.Object) ) { + println("YES") val range_symbol2 = s.Range(range_symbol.startLine, range_symbol.startCharacter - 4, range_symbol.endLine, @@ -535,7 +550,7 @@ class SemanticdbConsumer extends TastyConsumer { range_symbol2, true) - } else */ { + } else { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, range_symbol) @@ -551,12 +566,10 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Ident(name) => { - // To avoid adding the identifier of the package symbol - if (tree.symbol.owner.name != "") { - addOccurenceTree(tree, - 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(_) => @@ -575,6 +588,7 @@ class SemanticdbConsumer extends TastyConsumer { } } + println(root) Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index e2e2ca49a102..3800655ae0b1 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -177,14 +177,14 @@ class Tests { //@Test def testAccess(): Unit = checkFile("example/Access.scala") //@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") - @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - //WIP(assert because of case) @Test def testClasses(): Unit = checkFile("example/Classes.scala") + //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + //@Test def testClasses(): Unit = checkFile("example/Classes.scala") //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") //@Test def testExample(): Unit = checkFile("example/Example.scala") - //WIP @Test def testExample2(): Unit = checkFile("example/Example2.scala") + @Test def testExample2(): Unit = checkFile("example/Example2.scala") //@Test def testExclude(): Unit = checkFile("example/Exclude.scala") - //WIP(assert) @Test def testFlags(): Unit = checkFile("example/Flags.scala") + //WIP @Test def testFlags(): Unit = checkFile("example/Flags.scala") //@Test def testImports(): Unit = checkFile("example/Imports.scala") //@Test def testIssue1749(): Unit = checkFile("example/Issue1749.scala") //@Test def testLocalFile(): Unit = checkFile("example/local-file.scala") @@ -196,11 +196,11 @@ class Tests { //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") - //WIP(assert nodenotation owner) @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") + //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") - //WIP(assert) @Test def testTypes(): Unit = checkFile("example/Types.scala") + //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") //WIP @Test def testVals(): Unit = checkFile("example/Vals.scala") - @Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") + //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") def testOutput(className: String, expected: String): Unit = { From 71b9e3a7d443fb850a6a22d71bc970d4817a0dfc Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 28 Nov 2018 19:15:05 +0100 Subject: [PATCH 06/42] Add examples & add missing case for typetrees --- .../main/scala/example/IgnoredSymbol.scala | 8 ++++++++ .../main/scala/example/MultiArguments.scala | 5 +++++ .../dotty/semanticdb/SemanticdbConsumer.scala | 20 ++++++++++++------- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +++-- 4 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/IgnoredSymbol.scala create mode 100644 semanticdb/input/src/main/scala/example/MultiArguments.scala diff --git a/semanticdb/input/src/main/scala/example/IgnoredSymbol.scala b/semanticdb/input/src/main/scala/example/IgnoredSymbol.scala new file mode 100644 index 000000000000..13d58ac4132f --- /dev/null +++ b/semanticdb/input/src/main/scala/example/IgnoredSymbol.scala @@ -0,0 +1,8 @@ +package example + +class IgnoredTest { + val _ = 2 + def foo(x: Int): Unit = { + val _ = x + } +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/MultiArguments.scala b/semanticdb/input/src/main/scala/example/MultiArguments.scala new file mode 100644 index 000000000000..88d61b83585a --- /dev/null +++ b/semanticdb/input/src/main/scala/example/MultiArguments.scala @@ -0,0 +1,5 @@ +package example + +class Multiple { + def m19(x: Int)(z: Int = 3) = ??? +} diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 0f741b054a30..570f5d95818a 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -449,12 +449,15 @@ class SemanticdbConsumer extends TastyConsumer { }) } + def extractTypeTree(tree: TypeOrBoundsTree) = tree match { + case IsTypeTree(t) => t + } override def traverseTypeTree(tree: TypeOrBoundsTree)( implicit ctx: Context): Unit = { + println("type: ", tree) tree match { - case TypeTree.Ident(_) => { - tree match { - case IsTypeTree(typetree) => { + case TypeTree.Ident(_) => { + val typetree = extractTypeTree(tree) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, s.Range(typetree.pos.startLine, @@ -462,13 +465,16 @@ class SemanticdbConsumer extends TastyConsumer { typetree.pos.startLine, typetree.pos.endColumn)) } + case TypeTree.Select(qualifier, _) => { + val typetree = extractTypeTree(tree) + val range = rangeExclude(typetree.pos, qualifier.pos) + addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, range) + super.traverseTypeTree(typetree) + } case _ => super.traverseTypeTree(tree) - } - } - case _ => - super.traverseTypeTree(tree) } + } /*override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 3800655ae0b1..d5b01d7d0b26 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -182,7 +182,7 @@ class Tests { //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") //@Test def testExample(): Unit = checkFile("example/Example.scala") - @Test def testExample2(): Unit = checkFile("example/Example2.scala") + //@Test def testExample2(): Unit = checkFile("example/Example2.scala") //@Test def testExclude(): Unit = checkFile("example/Exclude.scala") //WIP @Test def testFlags(): Unit = checkFile("example/Flags.scala") //@Test def testImports(): Unit = checkFile("example/Imports.scala") @@ -191,7 +191,8 @@ class Tests { //@Test def testLocals(): Unit = checkFile("example/Locals.scala") //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") //WIP @Test def testMethods(): Unit = checkFile("example/Methods.scala") - //WIP @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") + //WIP @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") //@Test def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") From 35cfbd78a4fd7caa0b6861b3d4a2b33d9a961b2f Mon Sep 17 00:00:00 2001 From: poechsel Date: Fri, 30 Nov 2018 01:22:20 +0100 Subject: [PATCH 07/42] improvements on parametric class definition. Bugfix in TreeUtils --- .../tastyreflect/TypeOrBoundsOpsImpl.scala | 1 + .../scala/tasty/reflect/TypeOrBoundsOps.scala | 1 + .../main/scala/example/DependantModule.scala | 9 + .../dotty/semanticdb/SemanticdbConsumer.scala | 337 ++++++++++++++---- semanticdb/test/dotty/semanticdb/Tests.scala | 7 +- 5 files changed, 275 insertions(+), 80 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/DependantModule.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index f375733f5e00..52b103f3224b 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -23,6 +23,7 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI if (tpe.classSymbol.exists) Some(tpe.classSymbol.asClass) else None def typeSymbol(implicit ctx: Context): Symbol = tpe.typeSymbol + //def pos(implicit ctx: Context): Position = tpe.pos } def ConstantTypeDeco(x: ConstantType): Type.ConstantTypeAPI = new Type.ConstantTypeAPI { diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 1063d4345e25..41937d787ce7 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -55,6 +55,7 @@ trait TypeOrBoundsOps extends Core { def widen(implicit ctx: Context): Type def classSymbol(implicit ctx: Context): Option[ClassSymbol] def typeSymbol(implicit ctx: Context): Symbol + //def pos(implicit ctx: Context): Position } val IsType: IsTypeModule diff --git a/semanticdb/input/src/main/scala/example/DependantModule.scala b/semanticdb/input/src/main/scala/example/DependantModule.scala new file mode 100644 index 000000000000..bbb70c62727f --- /dev/null +++ b/semanticdb/input/src/main/scala/example/DependantModule.scala @@ -0,0 +1,9 @@ +package example + +class DepTemp() { + +} + +abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp { +val foo: List[Option[Option[X[C]]]] +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 570f5d95818a..fa3d61b0749c 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -27,6 +27,7 @@ class SemanticdbConsumer extends TastyConsumer { import reflect._ val symbolsCache: HashMap[Symbol, String] = HashMap() + val symbolPathsMap: Set[(String, s.Range)] = Set() object ChildTraverser extends TreeTraverser { var children: List[Tree] = Nil @@ -129,7 +130,8 @@ class SemanticdbConsumer extends TastyConsumer { val isEnumConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.flags.is(Flags.Enum) /*val isStaticConstructor = symbol.name == g.TermName("")*/ //val isClassfileAnnotationConstructor = symbol.owner.isClassfileAnnotation - isModuleConstructor || isTraitConstructor || isInterfaceConstructor || + /*isModuleConstructor || */ + isTraitConstructor || isInterfaceConstructor || isEnumConstructor /*|| isStaticConstructor || isClassfileAnnotationConstructor*/ } def isLocalChild(implicit ctx: Context): Boolean = @@ -201,6 +203,15 @@ class SemanticdbConsumer extends TastyConsumer { (symbol.isStaticMember && symbol.isClassConstructor) || (symbol.name == tpnme.STATIC_CONSTRUCTOR.toString) } + def isInitChild(implicit ctx: Context): Boolean = { + if (!(symbol.name == "" || symbol == NoSymbol) + && symbol.owner != NoSymbol) { + return symbol.owner.name == "" || symbol.owner.isInitChild + } else { + return false + } + } + def isWildCard(implicit ctx: Context): Boolean = { symbol.name.startsWith(tpnme.WILDCARD.toString) && symbol.name != tpnme.THIS.toString @@ -208,6 +219,7 @@ class SemanticdbConsumer extends TastyConsumer { def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || + //symbol.isInitChild || symbol.isWildCard || symbol.isAnonymousClass || symbol.isAnonymousFunction || @@ -273,7 +285,17 @@ class SemanticdbConsumer extends TastyConsumer { // relying on the name itself "" } else { - val previous_symbol = iterateParent(symbol.owner) + val previous_symbol = + /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, + The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. + This is illogic: a init method can't have any child. Thus, when the current symbol is + a typeparameter (or anything, but here it is just implemented for type parameter), and the owner + is an init, we can just "jump" over the init. */ + if (symbol.isTypeParameter && symbol.owner.name == "") + iterateParent(symbol.owner.owner) + else + iterateParent(symbol.owner) + val next_atom = if (symbol.isPackage) { d.Package(symbol.name) @@ -307,19 +329,18 @@ class SemanticdbConsumer extends TastyConsumer { def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - val symbol_path = + val (symbol_path, is_global) = if (symbol.isSemanticdbLocal) { if (symbolsCache.contains(symbol)) { - symbolsCache(symbol) + (symbolsCache(symbol), false) } else { var localsymbol = Symbols.Local(local_offset.toString) local_offset += 1 symbolsCache += (symbol -> localsymbol) - localsymbol + (localsymbol, false) } } else { - println("global: ", symbol) - iterateParent(symbol) + (iterateParent(symbol), true) } /*println(symbol, symbol.isUselessOccurrence) @@ -334,21 +355,30 @@ class SemanticdbConsumer extends TastyConsumer { println(symbol, "isSyntheticCas", symbol.isSyntheticCaseAccessor) println(symbol, "isRefinementCl", symbol.isRefinementClass) println(symbol, "isSyntheticJav", symbol.isSyntheticJavaModule)*/ + + println(symbol_path, range) if (symbol_path == "" || symbol.isUselessOccurrence) return - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - symbol_path, - type_symbol - ) + val key = (symbol_path, range) + if (!is_global || !(symbolPathsMap.contains(key))) { + if (is_global) { + symbolPathsMap += key + } + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + 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) } @@ -395,29 +425,21 @@ class SemanticdbConsumer extends TastyConsumer { range_end_column + offset) } - def rangeExclude(range: Position, exclude: Position): s.Range = { - def max(a: Int, b: Int): Int = { if (a > b) a else b } - return s.Range(exclude.endLine, - exclude.endColumn + 1, + def rangeSelect(name: String, range: Position): s.Range = { + val len = + if (name == "") 0 + else name.length + return s.Range(range.endLine, + range.endColumn - len, range.endLine, range.endColumn) } - def typetreeSymbol(tree: Tree, typetree: TypeTree): Unit = - typetree match { - case TypeTree.Inferred => () - case _ => - addOccurenceTypeTree( - typetree, - s.SymbolOccurrence.Role.REFERENCE, - range(tree, typetree.pos, typetree.symbol.name)) - } - def getImportPath(path_term: Term): String = { path_term match { case Term.Select(qualifier, selected) => { getImportPath(qualifier) - val range = rangeExclude(path_term.pos, qualifier.pos) + val range = rangeSelect(selected, path_term.pos) addOccurenceTree(path_term, s.SymbolOccurrence.Role.REFERENCE, range) @@ -452,57 +474,187 @@ class SemanticdbConsumer extends TastyConsumer { def extractTypeTree(tree: TypeOrBoundsTree) = tree match { case IsTypeTree(t) => t } - override def traverseTypeTree(tree: TypeOrBoundsTree)( - implicit ctx: Context): Unit = { - println("type: ", tree) + + /* + def traverseType(tree: TypeOrBounds)(implicit ctx: Context): Unit = { + //println(tree) tree match { - case TypeTree.Ident(_) => { - val typetree = extractTypeTree(tree) - addOccurenceTypeTree(typetree, - s.SymbolOccurrence.Role.REFERENCE, - s.Range(typetree.pos.startLine, - typetree.pos.startColumn, - typetree.pos.startLine, - typetree.pos.endColumn)) - } - case TypeTree.Select(qualifier, _) => { - val typetree = extractTypeTree(tree) - val range = rangeExclude(typetree.pos, qualifier.pos) - addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, range) - super.traverseTypeTree(typetree) - } - case _ => - super.traverseTypeTree(tree) + case IsType(t) => { + if(t.typeSymbol.pos.exists) + println(t.typeSymbol, t.typeSymbol.pos.startColumn, t.typeSymbol.pos.endColumn) + } + case _ => () } - - } - - /*override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { - println("CASE", tree) tree match { - case Pattern.Value(term) => { - term match { - - case Term.Ident(name) => { - // To avoid adding the identifier of the package symbol - if (term.symbol.owner.name != "") { - addOccurenceTree(term, - s.SymbolOccurrence.Role.REFERENCE, - range(term, term.pos, term.symbol.name)) - } - super.traversePattern(tree) + case Type.ConstantType(Constant.Symbol(symbol)) => + println("TYPE", symbol) + case Type.ConstantType(_) => {println("CNST")} + case Type.SymRef(_, a) => { + traverseType(a) } + case Type.TermRef(_, a) => { + traverseType(a) } + case Type.TypeRef(_, a) => { + traverseType(a) + } + case Type.SuperType(a, b) => { + traverseType(a) + traverseType(b) + } + case Type.Refinement(a, _, b) => { + traverseType(a) + traverseType(b) + } + case Type.AppliedType(a, l) => { + traverseType(a) + l.foreach(traverseType) + } + case Type.AnnotatedType(a, b) => { + traverseType(a) + super.traverseTree(b) + } + case Type.AndType(a, b) => { + traverseType(a) + traverseType(b) + } + case Type.OrType(a, b) => { + traverseType(a) + traverseType(b) + } + case Type.MatchType(a, b, l) => { + traverseType(a) + traverseType(b) + l.foreach(traverseType) + } + case Type.ByNameType(a) => { + traverseType(a) + } + case Type.ParamRef(a, _) => { + traverseType(a) + } + case Type.ThisType(a) => { + traverseType(a) + } + case Type.RecursiveThis(a) => { + traverseType(a) + } + case Type.RecursiveType(a) => { + traverseType(a) + } + case Type.MethodType(_, l, a) => { + traverseType(a) + l.foreach(traverseType) + } + case Type.PolyType(_, l, a) => { + traverseType(a) + l.foreach(traverseType) + } + case Type.TypeLambda(_, l, a) => { + traverseType(a) + l.foreach(traverseType) + } + case NoPrefix() => () + case TypeBounds(a, b) => { + traverseType(a) + traverseType(b) } - case _ => - super.traversePattern(tree) } }*/ + override def traverseTypeTree(tree: TypeOrBoundsTree)( + implicit ctx: Context): Unit = { + //println("--->", tree) + tree match { + case TypeTree.Ident(_) => { + val typetree = extractTypeTree(tree) + addOccurenceTypeTree(typetree, + s.SymbolOccurrence.Role.REFERENCE, + s.Range(typetree.pos.startLine, + typetree.pos.startColumn, + typetree.pos.startLine, + typetree.pos.endColumn)) + } + case TypeTree.Select(qualifier, _) => { + val typetree = extractTypeTree(tree) + val range = rangeSelect(typetree.symbol.name, typetree.pos) + addOccurenceTypeTree(typetree, + s.SymbolOccurrence.Role.REFERENCE, + range) + super.traverseTypeTree(typetree) + } + case _ => + super.traverseTypeTree(tree) + /*case TypeTree.Applied(_, _) => { + val typetree = extractTypeTree(tree) + println("APPLIED", typetree.pos.startColumn, typetree.pos.endColumn) + super.traverseTypeTree(tree) + } + case TypeTree.Synthetic() => { + val typetree = extractTypeTree(tree) + println("Synthetic", typetree.pos.startColumn, typetree.pos.endColumn) + super.traverseTypeTree(tree) + } + case TypeTree.Project(_, _) => { + println("Project") + super.traverseTypeTree(tree) + } + case TypeTree.Singleton(_) => { + println("Singleton") + super.traverseTypeTree(tree) + } + case TypeTree.Refined(_, _) => { + println("Refined") + super.traverseTypeTree(tree) + } + case TypeTree.Annotated(_, _) => { + println("Annotated") + super.traverseTypeTree(tree) + } + case TypeTree.And(_, _) => { + println("And") + super.traverseTypeTree(tree) + } + case TypeTree.Or(_, _) => { + println("Or") + super.traverseTypeTree(tree) + } + case TypeTree.MatchType(_, _, _) => { + println("MatchType") + super.traverseTypeTree(tree) + } + case TypeTree.ByName(_, _, _) => { + println("ByName") + super.traverseTypeTree(tree) + } + case TypeTree.TypeLambdaTree(_, _) => { + println("TypeLambdaTree") + super.traverseTypeTree(tree) + } + case TypeTree.Bind(_, _) => { + println("Bind") + super.traverseTypeTree(tree) + } + case TypeTree.Block(_, _) => { + println("Block") + super.traverseTypeTree(tree) + } + case SyntheticBounds() => { + println("synthetic bounds") + super.traverseTypeTree(tree) + } + case TypeBoundsTree(_, _) => { + //println("typetree") + super.traverseTypeTree(tree) + }*/ + } + + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - println("\n") + /*println("\n") println(tree) - println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn) + println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn)*/ tree match { case Import(path, selectors) => val key = (tree.symbol.name, tree.pos.start) @@ -510,6 +662,15 @@ class SemanticdbConsumer extends TastyConsumer { package_definitions += key getImportSelectors(getImportPath(path), selectors) } + case Term.New(_) => { + super.traverseTree(tree) + } + case Term.Apply(_, _) => { + println("---------------") + println(tree.symbol) + super.traverseTree(tree) + + } case IsDefinition(cdef) => { if (cdef.symbol.flags.is(Flags.Protected)) { @@ -545,12 +706,17 @@ class SemanticdbConsumer extends TastyConsumer { } if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - if (tree.symbol.name == "" && !tree.isUserCreated && !tree.symbol.owner.flags.is(Flags.Object) ) { - println("YES") - val range_symbol2 = s.Range(range_symbol.startLine, - range_symbol.startCharacter - 4, - range_symbol.endLine, - range_symbol.endCharacter - 4) + println(tree.symbol.flags) + println(tree.symbol.pos.startColumn, + tree.symbol.pos.endColumn, + tree.pos.startColumn, + tree.pos.endColumn) + if (tree.symbol.name == "" && (!tree.isUserCreated || tree.symbol.pos.startColumn == tree.symbol.pos.endColumn) && !tree.symbol.owner.flags.is(Flags.Object)) { + println("yes") + val range_symbol2 = s.Range(tree.pos.startLine, + tree.pos.endColumn, + tree.pos.endLine, + tree.pos.endColumn) addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, range_symbol2, @@ -566,18 +732,35 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Select(qualifier, _) => { - val range = rangeExclude(tree.pos, qualifier.pos) - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) + println("[Select] " + tree.symbol + " <-> " + qualifier) + val range = { + val r = rangeSelect(tree.symbol.name, tree.pos) + if (tree.symbol.name == "") + s.Range(r.startLine, + r.startCharacter + 1, + r.endLine, + r.endCharacter + 1) + else r + } + addOccurenceTree(tree, + s.SymbolOccurrence.Role.REFERENCE, + range, + false) super.traverseTree(tree) } case Term.Ident(name) => { + println("[Ident] " + name) addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range(tree, tree.pos, tree.symbol.name)) super.traverseTree(tree) } + + case Term.Literal(name) => { + println("Litteral: " + name) + } case PackageClause(_) => val key = (tree.symbol.name, tree.pos.start) if (!package_definitions(key)) { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index d5b01d7d0b26..31772d4e459b 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -191,8 +191,8 @@ class Tests { //@Test def testLocals(): Unit = checkFile("example/Locals.scala") //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") //WIP @Test def testMethods(): Unit = checkFile("example/Methods.scala") - //WIP @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") - @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") + //@Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") //@Test def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") @@ -200,7 +200,8 @@ class Tests { //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") - //WIP @Test def testVals(): Unit = checkFile("example/Vals.scala") + //@Test def testVals(): Unit = checkFile("example/Vals.scala") + @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") From a63dde55bd36ad80e04f3e67aa6b8ac2a5c2eea3 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 4 Dec 2018 01:03:00 +0100 Subject: [PATCH 08/42] adjustement for init symbol in class definition --- .../main/scala/example/DependantModule.scala | 2 +- .../src/main/scala/example/Example.scala | 2 +- .../input/src/main/scala/example/New.scala | 5 + .../dotty/semanticdb/SemanticdbConsumer.scala | 235 ++++-------------- semanticdb/test/dotty/semanticdb/Tests.scala | 1 + 5 files changed, 53 insertions(+), 192 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/New.scala diff --git a/semanticdb/input/src/main/scala/example/DependantModule.scala b/semanticdb/input/src/main/scala/example/DependantModule.scala index bbb70c62727f..104a0579fec0 100644 --- a/semanticdb/input/src/main/scala/example/DependantModule.scala +++ b/semanticdb/input/src/main/scala/example/DependantModule.scala @@ -1,6 +1,6 @@ package example -class DepTemp() { +class DepTemp { } diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index f4eb0fbeb947..4e431eccf47a 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -16,6 +16,6 @@ class Example { ) } -class ExampleInit() { +class ExampleInit { } \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/New.scala b/semanticdb/input/src/main/scala/example/New.scala new file mode 100644 index 000000000000..f166417736ef --- /dev/null +++ b/semanticdb/input/src/main/scala/example/New.scala @@ -0,0 +1,5 @@ +package example + +class TestNew extends C { + val b = new B +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index fa3d61b0749c..93dfeb490af3 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -342,19 +342,6 @@ class SemanticdbConsumer extends TastyConsumer { } else { (iterateParent(symbol), true) } - /*println(symbol, symbol.isUselessOccurrence) - - println(symbol, "isWildCard", symbol.isWildCard) - println(symbol, "isAnonymousCl", symbol.isAnonymousClass) - println(symbol, "isAnonymousFun", symbol.isAnonymousFunction) - println(symbol, "isSyntheticCon", symbol.isSyntheticConstructor) - println(symbol, "isStaticConstr", symbol.isStaticConstructor) - println(symbol, "isLocalChil", symbol.isLocalChild) - println(symbol, "isSyntheticVal", symbol.isSyntheticValueClassCompanion) - println(symbol, "isUselessFie", symbol.isUselessField) - println(symbol, "isSyntheticCas", symbol.isSyntheticCaseAccessor) - println(symbol, "isRefinementCl", symbol.isRefinementClass) - println(symbol, "isSyntheticJav", symbol.isSyntheticJavaModule)*/ println(symbol_path, range) if (symbol_path == "" || symbol.isUselessOccurrence) return @@ -378,8 +365,7 @@ class SemanticdbConsumer extends TastyConsumer { type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { - - if (tree.isUserCreated || force_add) { + if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``()."))) { addOccurence(tree.symbol, type_symbol, range) } } @@ -475,96 +461,8 @@ class SemanticdbConsumer extends TastyConsumer { case IsTypeTree(t) => t } - /* - def traverseType(tree: TypeOrBounds)(implicit ctx: Context): Unit = { - //println(tree) - tree match { - case IsType(t) => { - if(t.typeSymbol.pos.exists) - println(t.typeSymbol, t.typeSymbol.pos.startColumn, t.typeSymbol.pos.endColumn) - } - case _ => () - } - tree match { - case Type.ConstantType(Constant.Symbol(symbol)) => - println("TYPE", symbol) - case Type.ConstantType(_) => {println("CNST")} - case Type.SymRef(_, a) => { - traverseType(a) - } - case Type.TermRef(_, a) => { - traverseType(a) - } - case Type.TypeRef(_, a) => { - traverseType(a) - } - case Type.SuperType(a, b) => { - traverseType(a) - traverseType(b) - } - case Type.Refinement(a, _, b) => { - traverseType(a) - traverseType(b) - } - case Type.AppliedType(a, l) => { - traverseType(a) - l.foreach(traverseType) - } - case Type.AnnotatedType(a, b) => { - traverseType(a) - super.traverseTree(b) - } - case Type.AndType(a, b) => { - traverseType(a) - traverseType(b) - } - case Type.OrType(a, b) => { - traverseType(a) - traverseType(b) - } - case Type.MatchType(a, b, l) => { - traverseType(a) - traverseType(b) - l.foreach(traverseType) - } - case Type.ByNameType(a) => { - traverseType(a) - } - case Type.ParamRef(a, _) => { - traverseType(a) - } - case Type.ThisType(a) => { - traverseType(a) - } - case Type.RecursiveThis(a) => { - traverseType(a) - } - case Type.RecursiveType(a) => { - traverseType(a) - } - case Type.MethodType(_, l, a) => { - traverseType(a) - l.foreach(traverseType) - } - case Type.PolyType(_, l, a) => { - traverseType(a) - l.foreach(traverseType) - } - case Type.TypeLambda(_, l, a) => { - traverseType(a) - l.foreach(traverseType) - } - case NoPrefix() => () - case TypeBounds(a, b) => { - traverseType(a) - traverseType(b) - } - } - }*/ - override def traverseTypeTree(tree: TypeOrBoundsTree)( implicit ctx: Context): Unit = { - //println("--->", tree) tree match { case TypeTree.Ident(_) => { val typetree = extractTypeTree(tree) @@ -585,76 +483,13 @@ class SemanticdbConsumer extends TastyConsumer { } case _ => super.traverseTypeTree(tree) - /*case TypeTree.Applied(_, _) => { - val typetree = extractTypeTree(tree) - println("APPLIED", typetree.pos.startColumn, typetree.pos.endColumn) - super.traverseTypeTree(tree) - } - case TypeTree.Synthetic() => { - val typetree = extractTypeTree(tree) - println("Synthetic", typetree.pos.startColumn, typetree.pos.endColumn) - super.traverseTypeTree(tree) - } - case TypeTree.Project(_, _) => { - println("Project") - super.traverseTypeTree(tree) - } - case TypeTree.Singleton(_) => { - println("Singleton") - super.traverseTypeTree(tree) - } - case TypeTree.Refined(_, _) => { - println("Refined") - super.traverseTypeTree(tree) - } - case TypeTree.Annotated(_, _) => { - println("Annotated") - super.traverseTypeTree(tree) - } - case TypeTree.And(_, _) => { - println("And") - super.traverseTypeTree(tree) - } - case TypeTree.Or(_, _) => { - println("Or") - super.traverseTypeTree(tree) - } - case TypeTree.MatchType(_, _, _) => { - println("MatchType") - super.traverseTypeTree(tree) - } - case TypeTree.ByName(_, _, _) => { - println("ByName") - super.traverseTypeTree(tree) - } - case TypeTree.TypeLambdaTree(_, _) => { - println("TypeLambdaTree") - super.traverseTypeTree(tree) - } - case TypeTree.Bind(_, _) => { - println("Bind") - super.traverseTypeTree(tree) - } - case TypeTree.Block(_, _) => { - println("Block") - super.traverseTypeTree(tree) - } - case SyntheticBounds() => { - println("synthetic bounds") - super.traverseTypeTree(tree) - } - case TypeBoundsTree(_, _) => { - //println("typetree") - super.traverseTypeTree(tree) - }*/ } - } + var fittedInitClassRange: Option[s.Range] = None + var forceAddBecauseParents: Boolean = false + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { - /*println("\n") - println(tree) - println(tree.pos.startColumn, tree.symbol.name, tree.pos.endColumn)*/ tree match { case Import(path, selectors) => val key = (tree.symbol.name, tree.pos.start) @@ -662,15 +497,49 @@ class SemanticdbConsumer extends TastyConsumer { package_definitions += key getImportSelectors(getImportPath(path), selectors) } - case Term.New(_) => { + case Term.New(ty) => { super.traverseTree(tree) } case Term.Apply(_, _) => { - println("---------------") - println(tree.symbol) super.traverseTree(tree) } + case ClassDef(classname, constr, parents, selfopt, statements) => { + // we first add the class to the symbol list + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range(tree, tree.symbol.pos, tree.symbol.name)) + println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) + println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) + // then the constructor + if (!constr.isUserCreated) { + fittedInitClassRange = Some( + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1, + tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1)) + } else { + fittedInitClassRange = Some(s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) + } + traverseTree(constr) + fittedInitClassRange = None + + // we add the parents to the symbol list + forceAddBecauseParents = true + parents.foreach(_ match { + case IsTypeTree(t) => traverseTypeTree(t) + case IsTerm(t) => traverseTree(t) + }) + forceAddBecauseParents = false + + selfopt.foreach(traverseTree) + + + statements.foreach(traverseTree) + } case IsDefinition(cdef) => { if (cdef.symbol.flags.is(Flags.Protected)) { @@ -706,22 +575,13 @@ class SemanticdbConsumer extends TastyConsumer { } if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - println(tree.symbol.flags) - println(tree.symbol.pos.startColumn, - tree.symbol.pos.endColumn, - tree.pos.startColumn, - tree.pos.endColumn) - if (tree.symbol.name == "" && (!tree.isUserCreated || tree.symbol.pos.startColumn == tree.symbol.pos.endColumn) && !tree.symbol.owner.flags.is(Flags.Object)) { - println("yes") - val range_symbol2 = s.Range(tree.pos.startLine, - tree.pos.endColumn, - tree.pos.endLine, - tree.pos.endColumn) + if (tree.symbol.name == "" && tree.symbol.flags.is(Flags.Object)) { + + } else if (tree.symbol.name == "" && fittedInitClassRange != None) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, - range_symbol2, + fittedInitClassRange.get, true) - } else { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -732,7 +592,6 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Select(qualifier, _) => { - println("[Select] " + tree.symbol + " <-> " + qualifier) val range = { val r = rangeSelect(tree.symbol.name, tree.pos) if (tree.symbol.name == "") @@ -750,7 +609,6 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Ident(name) => { - println("[Ident] " + name) addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range(tree, tree.pos, tree.symbol.name)) @@ -758,9 +616,6 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(tree) } - case Term.Literal(name) => { - println("Litteral: " + name) - } case PackageClause(_) => val key = (tree.symbol.name, tree.pos.start) if (!package_definitions(key)) { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 31772d4e459b..e5290c9530a1 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -202,6 +202,7 @@ class Tests { //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") //@Test def testVals(): Unit = checkFile("example/Vals.scala") @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") + //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") From 9943adeab25187ca61822ae250b600b6887ba98c Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 9 Dec 2018 13:38:47 +0100 Subject: [PATCH 09/42] avoid generating init symbol for objects --- .../src/main/scala/example/Classes.scala | 7 +++--- .../dotty/semanticdb/SemanticdbConsumer.scala | 22 ++++++++++--------- semanticdb/test/dotty/semanticdb/Tests.scala | 8 +++---- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala index b82f2c030ff0..397fcd120ee6 100644 --- a/semanticdb/input/src/main/scala/example/Classes.scala +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -1,5 +1,5 @@ package example - +/* class C1(val x1: Int) extends AnyVal class C2(val x2: Int) extends AnyVal @@ -13,9 +13,9 @@ 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) @@ -31,3 +31,4 @@ object N { local + 2 } } +*/ \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 93dfeb490af3..c5d8c4da211f 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -124,6 +124,8 @@ class SemanticdbConsumer extends TastyConsumer { symbol.name == "" def isSyntheticConstructor(implicit ctx: Context): Boolean = { + val isObjectConstructor = symbol.isConstructor && symbol.owner != NoSymbol && symbol.owner.flags.is(Flags.Object) + //println("====>", symbol, symbol.owner, symbol.owner.flags, symbol.owner.flags.isObject, isObjectConstructor) val isModuleConstructor = symbol.isConstructor && symbol.owner.isClass val isTraitConstructor = symbol.isConstructor && symbol.owner.isTrait val isInterfaceConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.isTrait @@ -131,7 +133,7 @@ class SemanticdbConsumer extends TastyConsumer { /*val isStaticConstructor = symbol.name == g.TermName("")*/ //val isClassfileAnnotationConstructor = symbol.owner.isClassfileAnnotation /*isModuleConstructor || */ - isTraitConstructor || isInterfaceConstructor || + isTraitConstructor || isInterfaceConstructor || isObjectConstructor || isEnumConstructor /*|| isStaticConstructor || isClassfileAnnotationConstructor*/ } def isLocalChild(implicit ctx: Context): Boolean = @@ -289,9 +291,8 @@ class SemanticdbConsumer extends TastyConsumer { /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. This is illogic: a init method can't have any child. Thus, when the current symbol is - a typeparameter (or anything, but here it is just implemented for type parameter), and the owner - is an init, we can just "jump" over the init. */ - if (symbol.isTypeParameter && symbol.owner.name == "") + a typeparameter (or anything), and the owner is an init, we can just "jump" over the init. */ + if (symbol.owner.name == "") iterateParent(symbol.owner.owner) else iterateParent(symbol.owner) @@ -343,8 +344,8 @@ class SemanticdbConsumer extends TastyConsumer { (iterateParent(symbol), true) } - println(symbol_path, range) if (symbol_path == "" || symbol.isUselessOccurrence) return + println(symbol_path, range, symbol.owner.flags) val key = (symbol_path, range) if (!is_global || !(symbolPathsMap.contains(key))) { @@ -509,8 +510,8 @@ class SemanticdbConsumer extends TastyConsumer { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, range(tree, tree.symbol.pos, tree.symbol.name)) - println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) - println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) + //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) + //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) // then the constructor if (!constr.isUserCreated) { fittedInitClassRange = Some( @@ -575,8 +576,9 @@ class SemanticdbConsumer extends TastyConsumer { } if (tree.symbol.name != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - if (tree.symbol.name == "" && tree.symbol.flags.is(Flags.Object)) { - + //println(tree, tree.symbol.name, tree.symbol.owner, tree.symbol.owner.flags) + if (tree.symbol.name == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { + //println("omitting", tree.symbol.name) } else if (tree.symbol.name == "" && fittedInitClassRange != None) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -604,7 +606,7 @@ class SemanticdbConsumer extends TastyConsumer { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, - false) + true) super.traverseTree(tree) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index e5290c9530a1..dc0482a0bdfc 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -178,7 +178,7 @@ class Tests { //@Test def testAccess(): Unit = checkFile("example/Access.scala") //@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - //@Test def testClasses(): Unit = checkFile("example/Classes.scala") + @Test def testClasses(): Unit = checkFile("example/Classes.scala") //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") //@Test def testExample(): Unit = checkFile("example/Example.scala") @@ -190,10 +190,10 @@ class Tests { //@Test def testLocalFile(): Unit = checkFile("example/local-file.scala") //@Test def testLocals(): Unit = checkFile("example/Locals.scala") //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") - //WIP @Test def testMethods(): Unit = checkFile("example/Methods.scala") + //WIP(assert) @Test def testMethods(): Unit = checkFile("example/Methods.scala") //@Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") - //@Test def testObjects(): Unit = checkFile("example/Objects.scala") + //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") @@ -201,7 +201,7 @@ class Tests { //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") //@Test def testVals(): Unit = checkFile("example/Vals.scala") - @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") + //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") From a3ceec279623fdd9ba258abaad314ed357c43c6b Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 9 Dec 2018 21:43:57 +0100 Subject: [PATCH 10/42] self symbols --- .../src/main/scala/example/SelfUse.scala | 5 + .../input/src/main/scala/example/Selfs.scala | 4 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 171 +++++++++++------- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +- 4 files changed, 121 insertions(+), 64 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/SelfUse.scala diff --git a/semanticdb/input/src/main/scala/example/SelfUse.scala b/semanticdb/input/src/main/scala/example/SelfUse.scala new file mode 100644 index 000000000000..db6ee485e586 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/SelfUse.scala @@ -0,0 +1,5 @@ +package example + +class SelfUse extends B { a : B => +val c = a.b +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Selfs.scala b/semanticdb/input/src/main/scala/example/Selfs.scala index 06ed2c26709a..6226250d5452 100644 --- a/semanticdb/input/src/main/scala/example/Selfs.scala +++ b/semanticdb/input/src/main/scala/example/Selfs.scala @@ -1,6 +1,8 @@ package example -class B +class B { + val b = 4 +} class AC1 extends B { self => } diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index c5d8c4da211f..3896a05f97d6 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -21,12 +21,12 @@ class SemanticdbConsumer extends TastyConsumer { s.TextDocument(text = text, occurrences = occurrences) } val package_definitions: Set[Tuple2[String, Int]] = Set() + val symbolsCache: HashMap[(String, s.Range), String] = HashMap() var local_offset: Int = 0 final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ - val symbolsCache: HashMap[Symbol, String] = HashMap() val symbolPathsMap: Set[(String, s.Range)] = Set() object ChildTraverser extends TreeTraverser { @@ -278,71 +278,86 @@ class SemanticdbConsumer extends TastyConsumer { } def iterateParent(symbol: Symbol): String = { - if (symbolsCache.contains(symbol)) { - return symbolsCache(symbol) + if (symbol.name == "" || symbol.name == "") then { + // TODO had a "NoDenotation" test to avoid + // relying on the name itself + "" } else { - val out_symbol_path = - if (symbol.name == "" || symbol.name == "") then { - // TODO had a "NoDenotation" test to avoid - // relying on the name itself - "" - } else { - val previous_symbol = - /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, + val previous_symbol = + /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. This is illogic: a init method can't have any child. Thus, when the current symbol is a typeparameter (or anything), and the owner is an init, we can just "jump" over the init. */ - if (symbol.owner.name == "") - iterateParent(symbol.owner.owner) - else - iterateParent(symbol.owner) - - val next_atom = - if (symbol.isPackage) { - d.Package(symbol.name) - } else if (symbol.isObject) { - symbol match { - case IsClassSymbol(classsymbol) => - d.Term(resolveClass(classsymbol).name) - case _ => - d.Term(symbol.name) - } - } else if (symbol.isMethod) { - d.Method(symbol.name, - disimbiguate(previous_symbol + symbol.name, symbol)) - } else if (symbol.isTypeParameter) { - d.TypeParameter(symbol.name) - } else if (symbol.isValueParameter) { - d.Parameter(symbol.name) - } else if (symbol.isType || symbol.isTrait) { - d.Type(symbol.name) - } else { + if (symbol.owner.name == "") + iterateParent(symbol.owner.owner) + else + iterateParent(symbol.owner) + + val next_atom = + if (symbol.isPackage) { + d.Package(symbol.name) + } else if (symbol.isObject) { + symbol match { + case IsClassSymbol(classsymbol) => + d.Term(resolveClass(classsymbol).name) + case _ => d.Term(symbol.name) - } - - Symbols.Global(previous_symbol, next_atom) + } + } else if (symbol.isMethod) { + d.Method(symbol.name, + disimbiguate(previous_symbol + symbol.name, symbol)) + } else if (symbol.isTypeParameter) { + d.TypeParameter(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) } - symbolsCache += (symbol -> out_symbol_path) - out_symbol_path + + Symbols.Global(previous_symbol, next_atom) + } + } + + def addSelfDefinition(name: String, range: s.Range): Unit = { + var localsymbol = Symbols.Local(local_offset.toString) + local_offset += 1 + symbolsCache += ((name, range) -> localsymbol) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + localsymbol, + s.SymbolOccurrence.Role.DEFINITION + ) + } + + def symbolToSymbolString(symbol: Symbol): (String, Boolean) = { + if (symbol.isSemanticdbLocal) { + var localsymbol = Symbols.Local(local_offset.toString) + local_offset += 1 + (localsymbol, false) + } else { + (iterateParent(symbol), true) } } def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - val (symbol_path, is_global) = - if (symbol.isSemanticdbLocal) { - if (symbolsCache.contains(symbol)) { - (symbolsCache(symbol), false) - } else { - var localsymbol = Symbols.Local(local_offset.toString) - local_offset += 1 - symbolsCache += (symbol -> localsymbol) - (localsymbol, false) - } - } else { - (iterateParent(symbol), true) + val (symbol_path, is_global) = posToRange(symbol.pos) match { + case Some(keyRange) + if symbolsCache.contains((symbol.name, keyRange)) => + (symbolsCache((symbol.name, keyRange)), symbol.isSemanticdbLocal) + case Some(keyRange) => { + val (sp, ig) = symbolToSymbolString(symbol) + symbolsCache += ((symbol.name, keyRange) -> sp) + (sp, ig) } + case _ => + symbolToSymbolString(symbol) + } if (symbol_path == "" || symbol.isUselessOccurrence) return println(symbol_path, range, symbol.owner.flags) @@ -366,7 +381,8 @@ class SemanticdbConsumer extends TastyConsumer { type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { - if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``()."))) { + if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( + tree.symbol) == "java/lang/Object#``()."))) { addOccurence(tree.symbol, type_symbol, range) } } @@ -392,6 +408,18 @@ class SemanticdbConsumer extends TastyConsumer { ) } + def posToRange(pos: Position): Option[s.Range] = { + if (pos.exists) { + Some( + s.Range(pos.startLine, + pos.startColumn, + pos.startLine, + pos.endColumn)) + } else { + None + } + } + def range(tree: Tree, pos: Position, name: String): s.Range = { val offset = tree match { case IsPackageClause(tree) => "package ".length @@ -520,10 +548,11 @@ class SemanticdbConsumer extends TastyConsumer { tree.symbol.pos.startLine, tree.symbol.pos.startColumn + classname.length + 1)) } else { - fittedInitClassRange = Some(s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) + fittedInitClassRange = Some( + s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) } traverseTree(constr) fittedInitClassRange = None @@ -532,13 +561,30 @@ class SemanticdbConsumer extends TastyConsumer { forceAddBecauseParents = true parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) - case IsTerm(t) => traverseTree(t) + case IsTerm(t) => {println(t.pos.startColumn, t.pos.endColumn) + traverseTree(t)} }) forceAddBecauseParents = false - selfopt.foreach(traverseTree) + selfopt match { + case Some(vdef @ ValDef(name, _, _)) => { + val posColumn : Int = parents.foldLeft(vdef.pos.startColumn)((old : Int, ct : TermOrTypeTree) => + ct match { + case IsTerm(t) => if (t.pos.endColumn + 3 < old) {t.pos.endColumn+3} else {old} + case _ => old + }) + println(posColumn) + println(vdef) + println(vdef.pos.startColumn, tree.pos.startColumn, tree.pos.endColumn) + addSelfDefinition(name, s.Range(vdef.pos.startLine, posColumn, vdef.pos.endLine, posColumn + name.length)) + println(name) + } + case _ => + } + selfopt.foreach(traverseTree) + statements.foreach(traverseTree) } case IsDefinition(cdef) => { @@ -593,7 +639,10 @@ class SemanticdbConsumer extends TastyConsumer { super.traverseTree(cdef) } - case Term.Select(qualifier, _) => { + case Term.This(what) => + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, posToRange(tree.pos).get) + + case Term.Select(qualifier, _, _) => { val range = { val r = rangeSelect(tree.symbol.name, tree.pos) if (tree.symbol.name == "") diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index dc0482a0bdfc..03c59ed56a3a 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -178,7 +178,7 @@ class Tests { //@Test def testAccess(): Unit = checkFile("example/Access.scala") //@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - @Test def testClasses(): Unit = checkFile("example/Classes.scala") + //@Test def testClasses(): Unit = checkFile("example/Classes.scala") //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") //@Test def testExample(): Unit = checkFile("example/Example.scala") @@ -196,7 +196,8 @@ class Tests { //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") - //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + @Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + @Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") From 44d4cce1f5c03f12a8af99825c15cb5c8f576875 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 9 Dec 2018 23:23:55 +0100 Subject: [PATCH 11/42] Add support for pattern bind --- .../dotc/tastyreflect/PatternOpsImpl.scala | 1 + .../src/scala/tasty/reflect/PatternOps.scala | 2 + .../input/src/main/scala/example/Case.scala | 9 ++ .../dotty/semanticdb/SemanticdbConsumer.scala | 87 ++++++++++++++----- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +- 5 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/Case.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/PatternOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/PatternOpsImpl.scala index 88d6df9723dd..764a757e838b 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/PatternOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/PatternOpsImpl.scala @@ -36,6 +36,7 @@ trait PatternOpsImpl extends scala.tasty.reflect.PatternOps with CoreImpl { def PatternDeco(pattern: Pattern): PatternAPI = new PatternAPI { def pos(implicit ctx: Context): Position = pattern.sourcePos def tpe(implicit ctx: Context): Type = pattern.tpe.stripTypeVar + def symbol(implicit ctx: Context): Symbol = pattern.symbol } object Pattern extends PatternModule { diff --git a/library/src/scala/tasty/reflect/PatternOps.scala b/library/src/scala/tasty/reflect/PatternOps.scala index 0ad4d7307dd0..dd43db40f035 100644 --- a/library/src/scala/tasty/reflect/PatternOps.scala +++ b/library/src/scala/tasty/reflect/PatternOps.scala @@ -14,6 +14,8 @@ trait PatternOps extends Core { def pos(implicit ctx: Context): Position def tpe(implicit ctx: Context): Type + + def symbol(implicit ctx: Context): Symbol } implicit def PatternDeco(pattern: Pattern): PatternAPI diff --git a/semanticdb/input/src/main/scala/example/Case.scala b/semanticdb/input/src/main/scala/example/Case.scala new file mode 100644 index 000000000000..409c0f958e18 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Case.scala @@ -0,0 +1,9 @@ +package example + +class CaseTest { + def foo (x: Option[Int]) : Int = + x match { + case y @ Some(x) => x + case None => 0 + } +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 3896a05f97d6..68cd494de7bd 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -321,16 +321,16 @@ class SemanticdbConsumer extends TastyConsumer { } def addSelfDefinition(name: String, range: s.Range): Unit = { - var localsymbol = Symbols.Local(local_offset.toString) - local_offset += 1 - symbolsCache += ((name, range) -> localsymbol) - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - localsymbol, - s.SymbolOccurrence.Role.DEFINITION - ) + var localsymbol = Symbols.Local(local_offset.toString) + local_offset += 1 + symbolsCache += ((name, range) -> localsymbol) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + localsymbol, + s.SymbolOccurrence.Role.DEFINITION + ) } def symbolToSymbolString(symbol: Symbol): (String, Boolean) = { @@ -377,10 +377,14 @@ class SemanticdbConsumer extends TastyConsumer { } } + val reserverdFunctions: List[String] = "apply" :: "unapply" :: Nil def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { + if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reserverdFunctions + .contains(tree.symbol.name)) + return if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( tree.symbol) == "java/lang/Object#``()."))) { addOccurence(tree.symbol, type_symbol, range) @@ -515,6 +519,30 @@ class SemanticdbConsumer extends TastyConsumer { } } + override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { + tree match { + case Pattern.Bind(name, _) => { + println("[bind]", + tree.pos.startColumn, + tree.pos.endColumn, + tree.symbol, + tree.symbol.pos.startColumn, + tree.symbol.pos.endColumn) + addOccurence( + tree.symbol, + s.SymbolOccurrence.Role.REFERENCE, + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn, + tree.symbol.pos.endLine, + tree.symbol.pos.startColumn + name.length) + ) + super.traversePattern(tree) + } + case _ => + super.traversePattern(tree) + } + } + var fittedInitClassRange: Option[s.Range] = None var forceAddBecauseParents: Boolean = false @@ -531,7 +559,6 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.Apply(_, _) => { super.traverseTree(tree) - } case ClassDef(classname, constr, parents, selfopt, statements) => { // we first add the class to the symbol list @@ -561,27 +588,37 @@ class SemanticdbConsumer extends TastyConsumer { forceAddBecauseParents = true parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) - case IsTerm(t) => {println(t.pos.startColumn, t.pos.endColumn) - traverseTree(t)} + case IsTerm(t) => { + println(t.pos.startColumn, t.pos.endColumn) + traverseTree(t) + } }) forceAddBecauseParents = false - - selfopt match { case Some(vdef @ ValDef(name, _, _)) => { - val posColumn : Int = parents.foldLeft(vdef.pos.startColumn)((old : Int, ct : TermOrTypeTree) => - ct match { - case IsTerm(t) => if (t.pos.endColumn + 3 < old) {t.pos.endColumn+3} else {old} - case _ => old - }) + val posColumn: Int = parents.foldLeft(vdef.pos.startColumn)( + (old: Int, ct: TermOrTypeTree) => + ct match { + case IsTerm(t) => + if (t.pos.endColumn + 3 < old) { t.pos.endColumn + 3 } else { + old + } + case _ => old + }) println(posColumn) println(vdef) - println(vdef.pos.startColumn, tree.pos.startColumn, tree.pos.endColumn) - addSelfDefinition(name, s.Range(vdef.pos.startLine, posColumn, vdef.pos.endLine, posColumn + name.length)) + println(vdef.pos.startColumn, + tree.pos.startColumn, + tree.pos.endColumn) + addSelfDefinition(name, + s.Range(vdef.pos.startLine, + posColumn, + vdef.pos.endLine, + posColumn + name.length)) println(name) } - case _ => + case _ => } selfopt.foreach(traverseTree) @@ -640,7 +677,9 @@ class SemanticdbConsumer extends TastyConsumer { } case Term.This(what) => - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, posToRange(tree.pos).get) + addOccurenceTree(tree, + s.SymbolOccurrence.Role.REFERENCE, + posToRange(tree.pos).get) case Term.Select(qualifier, _, _) => { val range = { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 03c59ed56a3a..b17a6c00e409 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -196,8 +196,8 @@ class Tests { //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") - @Test def testSelfs(): Unit = checkFile("example/Selfs.scala") - @Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") + //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + //@Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") @@ -205,6 +205,7 @@ class Tests { //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") + @Test def testCase(): Unit = checkFile("example/Case.scala") def testOutput(className: String, expected: String): Unit = { From 7757bf6ad13c74794130cd4255be8f20d77d7a67 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 10 Dec 2018 01:18:45 +0100 Subject: [PATCH 12/42] improve class def & deal with default arguments --- .../input/src/main/scala/example/New.scala | 4 ++ .../dotty/semanticdb/SemanticdbConsumer.scala | 46 ++++++++++--------- semanticdb/test/dotty/semanticdb/Tests.scala | 4 +- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/New.scala b/semanticdb/input/src/main/scala/example/New.scala index f166417736ef..ee76dc8e2d00 100644 --- a/semanticdb/input/src/main/scala/example/New.scala +++ b/semanticdb/input/src/main/scala/example/New.scala @@ -1,5 +1,9 @@ package example +class Bonjour() { +} + class TestNew extends C { val b = new B + val c = new Bonjour } \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 68cd494de7bd..99e563ac3f40 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -91,6 +91,8 @@ class SemanticdbConsumer extends TastyConsumer { case _ => false } + def isDefaultGetter: Boolean = symbol.name.contains(tpnme.DEFAULT_GETTER.toString) + def isParameter: Boolean = symbol.flags.is(Flags.Param) def isObject: Boolean = symbol.flags.is(Flags.Object) @@ -222,6 +224,7 @@ class SemanticdbConsumer extends TastyConsumer { def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || //symbol.isInitChild || + symbol.isDefaultGetter || symbol.isWildCard || symbol.isAnonymousClass || symbol.isAnonymousFunction || @@ -303,7 +306,7 @@ class SemanticdbConsumer extends TastyConsumer { case _ => d.Term(symbol.name) } - } else if (symbol.isMethod) { + } else if (symbol.isMethod || symbol.isUsefulField) { d.Method(symbol.name, disimbiguate(previous_symbol + symbol.name, symbol)) } else if (symbol.isTypeParameter) { @@ -358,23 +361,28 @@ class SemanticdbConsumer extends TastyConsumer { case _ => symbolToSymbolString(symbol) } - if (symbol_path == "" || symbol.isUselessOccurrence) return - println(symbol_path, range, symbol.owner.flags) val key = (symbol_path, range) - if (!is_global || !(symbolPathsMap.contains(key))) { - if (is_global) { - symbolPathsMap += key - } - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - symbol_path, - type_symbol - ) - } + // TODO: refactor the following + + // this is to avoid duplicates symbols + // For example, when we define a class as: `class foo(x: Int)`, + // dotty will generate a ValDef for the x, but the x will also + // be present in the constructor, thus making a double definition + if (symbolPathsMap.contains(key)) return + if (is_global) { + symbolPathsMap += key + println("duplicates", key) + } + println(symbol_path, range, symbol.owner.flags, is_global) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + symbol_path, + type_symbol + ) } val reserverdFunctions: List[String] = "apply" :: "unapply" :: Nil @@ -522,12 +530,6 @@ class SemanticdbConsumer extends TastyConsumer { override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { tree match { case Pattern.Bind(name, _) => { - println("[bind]", - tree.pos.startColumn, - tree.pos.endColumn, - tree.symbol, - tree.symbol.pos.startColumn, - tree.symbol.pos.endColumn) addOccurence( tree.symbol, s.SymbolOccurrence.Role.REFERENCE, @@ -681,7 +683,7 @@ class SemanticdbConsumer extends TastyConsumer { s.SymbolOccurrence.Role.REFERENCE, posToRange(tree.pos).get) - case Term.Select(qualifier, _, _) => { + case Term.Select(qualifier, _) => { val range = { val r = rangeSelect(tree.symbol.name, tree.pos) if (tree.symbol.name == "") diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index b17a6c00e409..2f00b87346d0 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -191,7 +191,7 @@ class Tests { //@Test def testLocals(): Unit = checkFile("example/Locals.scala") //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") //WIP(assert) @Test def testMethods(): Unit = checkFile("example/Methods.scala") - //@Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") @@ -205,7 +205,7 @@ class Tests { //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") - @Test def testCase(): Unit = checkFile("example/Case.scala") + //@Test def testCase(): Unit = checkFile("example/Case.scala") def testOutput(className: String, expected: String): Unit = { From 4a685083755832c3a2ae259a084f5c8ce64d589f Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 10 Dec 2018 11:50:32 +0100 Subject: [PATCH 13/42] improve position for self symbols --- .../dotty/semanticdb/SemanticdbConsumer.scala | 33 ++++++++++--------- semanticdb/test/dotty/semanticdb/Tests.scala | 15 ++------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 99e563ac3f40..33df3e4f7b1a 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -11,7 +11,9 @@ import scala.meta.internal.{semanticdb => s} import dotty.semanticdb.Scala.{Descriptor => d} import dotty.semanticdb.Scala._ -class SemanticdbConsumer extends TastyConsumer { +import scala.io.Source + +class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { var stack: List[String] = Nil val semantic: s.TextDocument = s.TextDocument() @@ -24,6 +26,8 @@ class SemanticdbConsumer extends TastyConsumer { val symbolsCache: HashMap[(String, s.Range), String] = HashMap() var local_offset: Int = 0 + val sourceCode = Source.fromFile(sourceFile.toFile).mkString + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -373,7 +377,6 @@ class SemanticdbConsumer extends TastyConsumer { if (symbolPathsMap.contains(key)) return if (is_global) { symbolPathsMap += key - println("duplicates", key) } println(symbol_path, range, symbol.owner.flags, is_global) occurrences = @@ -591,34 +594,34 @@ class SemanticdbConsumer extends TastyConsumer { parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) case IsTerm(t) => { - println(t.pos.startColumn, t.pos.endColumn) traverseTree(t) } }) forceAddBecauseParents = false selfopt match { - case Some(vdef @ ValDef(name, _, _)) => { - val posColumn: Int = parents.foldLeft(vdef.pos.startColumn)( + case Some(vdef @ ValDef(name, _, _)) if name != "_" => { + // To find the current position, we will heuristically + // reparse the source code. + // The process is done in three steps: + // 1) Find a position before the '{' of the self but after any + // non related '{'. Here, it will be the largest end pos of a parent + // 2) Find the first '{' + // 3) Iterate until the character we are seeing is a letter + val startPosSearch: Int = parents.foldLeft(tree.pos.endColumn)( (old: Int, ct: TermOrTypeTree) => ct match { - case IsTerm(t) => - if (t.pos.endColumn + 3 < old) { t.pos.endColumn + 3 } else { - old - } + case IsTerm(t) if t.pos.endColumn < old => t.pos.endColumn case _ => old }) - println(posColumn) - println(vdef) - println(vdef.pos.startColumn, - tree.pos.startColumn, - tree.pos.endColumn) + var posColumn = sourceCode.indexOf("{", startPosSearch) + while (!sourceCode(posColumn).isLetter && posColumn < sourceCode.length) posColumn += 1 + addSelfDefinition(name, s.Range(vdef.pos.startLine, posColumn, vdef.pos.endLine, posColumn + name.length)) - println(name) } case _ => } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 2f00b87346d0..b2cd2be38d5d 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -129,7 +129,7 @@ class Tests { scalac } else { val (classpaths, classnames) = tasty_classes.unzip - val sdbconsumer = new SemanticdbConsumer + val sdbconsumer = new SemanticdbConsumer(scalaFile) val _ = ConsumeTasty(classpaths.head, classnames, sdbconsumer) sdbconsumer.toSemanticdb(scalac.text) @@ -191,12 +191,12 @@ class Tests { //@Test def testLocals(): Unit = checkFile("example/Locals.scala") //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") //WIP(assert) @Test def testMethods(): Unit = checkFile("example/Methods.scala") - @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + //@Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") - //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + @Test def testSelfs(): Unit = checkFile("example/Selfs.scala") //@Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") @@ -206,13 +206,4 @@ class Tests { //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") //@Test def testCase(): Unit = checkFile("example/Case.scala") - - - def testOutput(className: String, expected: String): Unit = { - val out = new StringBuilder - ConsumeTasty(tastyClassDirectory, List(className), new SemanticdbConsumer { - override def println(x: Any): Unit = out.append(x).append(";") - }) - assertEquals(expected, out.result()) - } } From 0d460cbd33a9208818665d421ff0f6276d9d7ee2 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 10 Dec 2018 19:10:31 +0100 Subject: [PATCH 14/42] Implement case class and case object --- .../input/src/main/scala/example/Case.scala | 5 +- .../src/main/scala/example/Classes.scala | 10 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 158 +++++++++++------- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +- 4 files changed, 112 insertions(+), 66 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Case.scala b/semanticdb/input/src/main/scala/example/Case.scala index 409c0f958e18..6aebcb750088 100644 --- a/semanticdb/input/src/main/scala/example/Case.scala +++ b/semanticdb/input/src/main/scala/example/Case.scala @@ -6,4 +6,7 @@ class CaseTest { case y @ Some(x) => x case None => 0 } -} \ No newline at end of file +} + +case class CaseClass(x: Int) +case object CaseObject \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala index 397fcd120ee6..fe428b860559 100644 --- a/semanticdb/input/src/main/scala/example/Classes.scala +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -1,9 +1,9 @@ package example -/* + class C1(val x1: Int) extends AnyVal -class C2(val x2: Int) extends AnyVal -object C2 +//class C2(val x2: Int) extends AnyVal +/*object C2 case class C3(x: Int) @@ -13,9 +13,9 @@ 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) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 33df3e4f7b1a..e30a7e70237b 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -95,7 +95,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => false } - def isDefaultGetter: Boolean = symbol.name.contains(tpnme.DEFAULT_GETTER.toString) + def isDefaultGetter: Boolean = + symbol.name.contains(tpnme.DEFAULT_GETTER.toString) def isParameter: Boolean = symbol.flags.is(Flags.Param) @@ -388,12 +389,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } - val reserverdFunctions: List[String] = "apply" :: "unapply" :: Nil + val reservedFunctions: List[String] = "apply" :: "unapply" :: Nil def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { - if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reserverdFunctions + if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reservedFunctions .contains(tree.symbol.name)) return if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( @@ -566,68 +567,109 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(tree) } case ClassDef(classname, constr, parents, selfopt, statements) => { - // we first add the class to the symbol list - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.name)) - //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) - //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) - // then the constructor - if (!constr.isUserCreated) { - fittedInitClassRange = Some( - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1, - tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1)) + println("\n") + val resolvedClassSymbol = tree.symbol.asClass.companionClass + val resolvedObjectSymbol = tree.symbol.asClass.companionModule + println(tree.symbol.flags) + if (resolvedClassSymbol != None && resolvedClassSymbol.get.flags.isCase) { + // case class + if (resolvedClassSymbol.get == tree.symbol) { + println("YES") + // we first add the class to the symbol list + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range(tree, tree.symbol.pos, tree.symbol.name)) + + fittedInitClassRange = Some( + s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) + + traverseTree(constr) + fittedInitClassRange = None + } else { + println("NO") + } + } else if (tree.symbol.flags.isObject && tree.symbol.flags.isCase) { + println("YES object") + // we first add the class to the symbol list + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range(tree, tree.symbol.pos, tree.symbol.name)) + + fittedInitClassRange = Some( + s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) + + traverseTree(constr) + fittedInitClassRange = None + println("NO object") } else { - fittedInitClassRange = Some( - s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) - } - traverseTree(constr) - fittedInitClassRange = None - - // we add the parents to the symbol list - forceAddBecauseParents = true - parents.foreach(_ match { - case IsTypeTree(t) => traverseTypeTree(t) - case IsTerm(t) => { - traverseTree(t) + // we first add the class to the symbol list + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range(tree, tree.symbol.pos, tree.symbol.name)) + //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) + //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) + // then the constructor + if (!constr.isUserCreated) { + fittedInitClassRange = Some( + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1, + tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1)) + } else { + fittedInitClassRange = Some( + s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) } - }) - forceAddBecauseParents = false - - selfopt match { - case Some(vdef @ ValDef(name, _, _)) if name != "_" => { - // To find the current position, we will heuristically - // reparse the source code. - // The process is done in three steps: - // 1) Find a position before the '{' of the self but after any - // non related '{'. Here, it will be the largest end pos of a parent - // 2) Find the first '{' - // 3) Iterate until the character we are seeing is a letter - val startPosSearch: Int = parents.foldLeft(tree.pos.endColumn)( - (old: Int, ct: TermOrTypeTree) => + traverseTree(constr) + fittedInitClassRange = None + + // we add the parents to the symbol list + forceAddBecauseParents = true + parents.foreach(_ match { + case IsTypeTree(t) => traverseTypeTree(t) + case IsTerm(t) => { + traverseTree(t) + } + }) + forceAddBecauseParents = false + + selfopt match { + case Some(vdef @ ValDef(name, _, _)) if name != "_" => { + // To find the current position, we will heuristically + // reparse the source code. + // The process is done in three steps: + // 1) Find a position before the '{' of the self but after any + // non related '{'. Here, it will be the largest end pos of a parent + // 2) Find the first '{' + // 3) Iterate until the character we are seeing is a letter + val startPosSearch: Int = parents.foldLeft( + tree.pos.endColumn)((old: Int, ct: TermOrTypeTree) => ct match { case IsTerm(t) if t.pos.endColumn < old => t.pos.endColumn - case _ => old + case _ => old }) - var posColumn = sourceCode.indexOf("{", startPosSearch) - while (!sourceCode(posColumn).isLetter && posColumn < sourceCode.length) posColumn += 1 - - addSelfDefinition(name, - s.Range(vdef.pos.startLine, - posColumn, - vdef.pos.endLine, - posColumn + name.length)) + var posColumn = sourceCode.indexOf("{", startPosSearch) + while (!sourceCode(posColumn).isLetter && posColumn < sourceCode.length) posColumn += 1 + + addSelfDefinition(name, + s.Range(vdef.pos.startLine, + posColumn, + vdef.pos.endLine, + posColumn + name.length)) + } + case _ => } - case _ => - } - selfopt.foreach(traverseTree) - statements.foreach(traverseTree) + statements.foreach(traverseTree) + } } case IsDefinition(cdef) => { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index b2cd2be38d5d..9fbb4bdcd732 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -196,7 +196,7 @@ class Tests { //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") - @Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") //@Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") @@ -205,5 +205,6 @@ class Tests { //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") - //@Test def testCase(): Unit = checkFile("example/Case.scala") + @Test def testCase(): Unit = checkFile("example/Case.scala") + //@Test def testApply(): Unit = checkFile("example/Apply.scala") } From 70fc3b682ca9c472f2928ecf83329b4da2e549d6 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 10 Dec 2018 23:42:48 +0100 Subject: [PATCH 15/42] fix several bugs --- .../src/main/scala/example/Classes.scala | 5 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 91 ++++++++----------- semanticdb/test/dotty/semanticdb/Tests.scala | 8 +- 3 files changed, 45 insertions(+), 59 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala index fe428b860559..b82f2c030ff0 100644 --- a/semanticdb/input/src/main/scala/example/Classes.scala +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -2,8 +2,8 @@ package example class C1(val x1: Int) extends AnyVal -//class C2(val x2: Int) extends AnyVal -/*object C2 +class C2(val x2: Int) extends AnyVal +object C2 case class C3(x: Int) @@ -31,4 +31,3 @@ object N { local + 2 } } -*/ \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index e30a7e70237b..49bcb7c72f9e 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -70,6 +70,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + implicit class PatternExtender(tree: Pattern) { + def isUserCreated: Boolean = { + return !(tree.pos.exists && tree.pos.start == tree.pos.end) + } + } + implicit class SymbolExtender(symbol: Symbol) { def isClass: Boolean = symbol match { case IsClassSymbol(_) => true @@ -251,9 +257,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def resolveClass(symbol: ClassSymbol): Symbol = (symbol.companionClass, symbol.companionModule) match { + case (Some(c), _) => c case (_, Some(module)) if symbol.flags.is(Flags.Object) => module - case (Some(c), _) => c - case _ => symbol + case _ => symbol } def disimbiguate(symbol_path: String, symbol: Symbol): String = { @@ -379,7 +385,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (is_global) { symbolPathsMap += key } - println(symbol_path, range, symbol.owner.flags, is_global) + println(symbol_path, range, symbol.owner.flags, is_global, iterateParent(symbol)) occurrences = occurrences :+ s.SymbolOccurrence( @@ -389,7 +395,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } - val reservedFunctions: List[String] = "apply" :: "unapply" :: Nil + val reservedFunctions: List[String] = Nil def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, @@ -397,6 +403,16 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reservedFunctions .contains(tree.symbol.name)) return + /*println(tree.isUserCreated, iterateParent(tree.symbol), force_add) + + val children: List[Position] = + ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) + println("#####", tree.pos.start, tree.pos.end) + if (tree.symbol.pos.exists) { + println("#####", tree.symbol.pos.start, tree.symbol.pos.end, tree, tree.symbol.name) + + } + children.foreach(p => println(p.start, p.end))*/ if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( tree.symbol) == "java/lang/Object#``()."))) { addOccurence(tree.symbol, type_symbol, range) @@ -405,10 +421,18 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { + println(typetree) if (typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } } + def addOccurencePatternTree(tree: Pattern, + type_symbol: s.SymbolOccurrence.Role, + range: s.Range): Unit = { + if (tree.isUserCreated) { + addOccurence(tree.symbol, type_symbol, range) + } + } def addOccurenceId(parent_path: String, id: Id): Unit = { val symbol_path = Symbols.Global(parent_path, d.Term(id.name)) occurrences = @@ -490,15 +514,16 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { selectors: List[ImportSelector]): Unit = { selectors.foreach(selector => selector match { - case SimpleSelector(id) => { + case SimpleSelector(id) if id.name != "_" => { addOccurenceId(parent_path, id) } - case RenameSelector(id, _) => { + case RenameSelector(id, _) if id.name != "_" => { addOccurenceId(parent_path, id) } - case OmitSelector(id) => { + case OmitSelector(id) if id.name != "_" => { addOccurenceId(parent_path, id) } + case _ => }) } @@ -526,6 +551,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { range) super.traverseTypeTree(typetree) } + case TypeTree.Inferred() => { + val typetree = extractTypeTree(tree) + addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, posToRange(typetree.pos).get) + } case _ => super.traverseTypeTree(tree) } @@ -534,8 +563,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { tree match { case Pattern.Bind(name, _) => { - addOccurence( - tree.symbol, + addOccurencePatternTree(tree, s.SymbolOccurrence.Role.REFERENCE, s.Range(tree.symbol.pos.startLine, tree.symbol.pos.startColumn, @@ -567,47 +595,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(tree) } case ClassDef(classname, constr, parents, selfopt, statements) => { - println("\n") - val resolvedClassSymbol = tree.symbol.asClass.companionClass - val resolvedObjectSymbol = tree.symbol.asClass.companionModule - println(tree.symbol.flags) - if (resolvedClassSymbol != None && resolvedClassSymbol.get.flags.isCase) { - // case class - if (resolvedClassSymbol.get == tree.symbol) { - println("YES") - // we first add the class to the symbol list - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.name)) - fittedInitClassRange = Some( - s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) - - traverseTree(constr) - fittedInitClassRange = None - } else { - println("NO") - } - } else if (tree.symbol.flags.isObject && tree.symbol.flags.isCase) { - println("YES object") - // we first add the class to the symbol list - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.name)) - - fittedInitClassRange = Some( - s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) - - traverseTree(constr) - fittedInitClassRange = None - println("NO object") - } else { // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -669,7 +657,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } statements.foreach(traverseTree) - } + } case IsDefinition(cdef) => { @@ -740,8 +728,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, - range, - true) + range) super.traverseTree(tree) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 9fbb4bdcd732..4da0f603b649 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -195,16 +195,16 @@ class Tests { //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") //def testObjects(): Unit = checkFile("example/Objects.scala") //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") - //WIP @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") + @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") //@Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") - //WIP @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") + //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") - //WIP @Test def testTypes(): Unit = checkFile("example/Types.scala") + //@Test def testTypes(): Unit = checkFile("example/Types.scala") //@Test def testVals(): Unit = checkFile("example/Vals.scala") //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") //@Test def testNew(): Unit = checkFile("example/New.scala") //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") - @Test def testCase(): Unit = checkFile("example/Case.scala") + //@Test def testCase(): Unit = checkFile("example/Case.scala") //@Test def testApply(): Unit = checkFile("example/Apply.scala") } From 88871af0b3fe961b0569145c472518d15645adf6 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 10 Dec 2018 23:43:08 +0100 Subject: [PATCH 16/42] Add missing test file --- semanticdb/input/src/main/scala/example/Apply.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 semanticdb/input/src/main/scala/example/Apply.scala diff --git a/semanticdb/input/src/main/scala/example/Apply.scala b/semanticdb/input/src/main/scala/example/Apply.scala new file mode 100644 index 000000000000..72f84468ba31 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Apply.scala @@ -0,0 +1,9 @@ +package example + +class TestApply { + object Foo { + def apply(x: Int) : Int = x + } + val z = Foo(1) + val y = Foo.apply(1) +} \ No newline at end of file From 7ae6c6e52052f02bad5a3f8d9e38073033f2f616 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 11 Dec 2018 18:35:32 +0100 Subject: [PATCH 17/42] Fix issue with non jvm compatible characters --- .../src/main/scala/example/BinaryOp.scala | 5 + .../src/main/scala/example/MethodUsages.scala | 6 +- .../src/main/scala/example/Synthetic.scala | 1 - .../src/main/scala/example/TypeBug.scala | 15 ++ .../dotty/semanticdb/SemanticdbConsumer.scala | 210 ++++++++++-------- .../test/dotty/semanticdb/Semanticdbs.scala | 10 +- semanticdb/test/dotty/semanticdb/Tests.scala | 62 +++--- 7 files changed, 179 insertions(+), 130 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/BinaryOp.scala create mode 100644 semanticdb/input/src/main/scala/example/TypeBug.scala diff --git a/semanticdb/input/src/main/scala/example/BinaryOp.scala b/semanticdb/input/src/main/scala/example/BinaryOp.scala new file mode 100644 index 000000000000..8a67f508c0f7 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/BinaryOp.scala @@ -0,0 +1,5 @@ +package example + +class BinaryOp { + 1 #:: 2 #:: Stream.empty +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/MethodUsages.scala b/semanticdb/input/src/main/scala/example/MethodUsages.scala index ea0b9e93713f..bcec38de7d3f 100644 --- a/semanticdb/input/src/main/scala/example/MethodUsages.scala +++ b/semanticdb/input/src/main/scala/example/MethodUsages.scala @@ -2,7 +2,7 @@ package example class MethodUsages { val m = new Methods[Int] - m.m1 + /*m.m1 m.m2() m.m3(0) m.m4(0)(0) @@ -13,7 +13,7 @@ class MethodUsages { m.m6(Nil) m.m7a(m, new m.List[Int]) m.m7b(new m.List[Int]) - m.`m8().`() + */m.`m8().`()/* m.m9(null) m.m10(null) m.m11(Predef) @@ -29,5 +29,5 @@ class MethodUsages { m.m17("") m.m18.m() m.m18(1) - m.m18("") + m.m18("")*/ } diff --git a/semanticdb/input/src/main/scala/example/Synthetic.scala b/semanticdb/input/src/main/scala/example/Synthetic.scala index c888edc329e8..c0aaea853eff 100644 --- a/semanticdb/input/src/main/scala/example/Synthetic.scala +++ b/semanticdb/input/src/main/scala/example/Synthetic.scala @@ -43,5 +43,4 @@ class Synthetic { b <- scala.concurrent.Future.successful(2) if a < b } yield a - } diff --git a/semanticdb/input/src/main/scala/example/TypeBug.scala b/semanticdb/input/src/main/scala/example/TypeBug.scala new file mode 100644 index 000000000000..9b8226502269 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/TypeBug.scala @@ -0,0 +1,15 @@ +package example + +import scala.language.existentials +import scala.language.higherKinds + +object TypBug { + class M { + def m: Int = ??? + } + class C extends M { + case class RepeatedType(s: String*) { + def m1(x: Int*): Int = s.length + } + } +} diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 49bcb7c72f9e..7c9dac33b617 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -77,6 +77,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } implicit class SymbolExtender(symbol: Symbol) { + def trueName: String = { + val prohibitedChars = '.' :: ';' :: '[' :: '/' :: '<' :: '>' :: Nil + //val prohibitedHashMap = prohibitedChars.map(x => x -> "$u%04X".format(x.toInt)).toMap + prohibitedChars.foldLeft(symbol.name)((old, chr) => + old.replaceAll("\\$u%04X".format(chr.toInt), chr.toString) + ) + } + + def isClass: Boolean = symbol match { case IsClassSymbol(_) => true case _ => false @@ -185,7 +194,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbol.isScalacField && !symbol.isUselessField } def isSyntheticCaseAccessor(implicit ctx: Context): Boolean = { - symbol.flags.is(Flags.CaseAcessor) && symbol.name.contains("$") + symbol.flags.is(Flags.CaseAcessor) && symbol.trueName.contains("$") } def isSyntheticJavaModule(implicit ctx: Context): Boolean = { !symbol.flags.is(Flags.Package) && symbol.flags.is(Flags.JavaDefined) && symbol.flags.is(Flags.Object) @@ -309,25 +318,25 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val next_atom = if (symbol.isPackage) { - d.Package(symbol.name) + d.Package(symbol.trueName) } else if (symbol.isObject) { symbol match { case IsClassSymbol(classsymbol) => - d.Term(resolveClass(classsymbol).name) + d.Term(resolveClass(classsymbol).trueName) case _ => - d.Term(symbol.name) + d.Term(symbol.trueName) } } else if (symbol.isMethod || symbol.isUsefulField) { - d.Method(symbol.name, - disimbiguate(previous_symbol + symbol.name, symbol)) + d.Method(symbol.trueName, + disimbiguate(previous_symbol + symbol.trueName, symbol)) } else if (symbol.isTypeParameter) { - d.TypeParameter(symbol.name) + d.TypeParameter(symbol.trueName) } else if (symbol.isValueParameter) { - d.Parameter(symbol.name) + d.Parameter(symbol.trueName) } else if (symbol.isType || symbol.isTrait) { - d.Type(symbol.name) + d.Type(symbol.trueName) } else { - d.Term(symbol.name) + d.Term(symbol.trueName) } Symbols.Global(previous_symbol, next_atom) @@ -360,13 +369,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { + if (symbol.name == "") return + val (symbol_path, is_global) = posToRange(symbol.pos) match { case Some(keyRange) - if symbolsCache.contains((symbol.name, keyRange)) => - (symbolsCache((symbol.name, keyRange)), symbol.isSemanticdbLocal) + if symbolsCache.contains((symbol.trueName, keyRange)) => + (symbolsCache((symbol.trueName, keyRange)), symbol.isSemanticdbLocal) case Some(keyRange) => { val (sp, ig) = symbolToSymbolString(symbol) - symbolsCache += ((symbol.name, keyRange) -> sp) + symbolsCache += ((symbol.trueName, keyRange) -> sp) (sp, ig) } case _ => @@ -385,7 +396,11 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (is_global) { symbolPathsMap += key } - println(symbol_path, range, symbol.owner.flags, is_global, iterateParent(symbol)) + println(symbol_path, + range, + symbol.owner.flags, + is_global, + iterateParent(symbol)) occurrences = occurrences :+ s.SymbolOccurrence( @@ -395,13 +410,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } - val reservedFunctions: List[String] = Nil + val reservedFunctions: List[String] = Nil def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reservedFunctions - .contains(tree.symbol.name)) + .contains(tree.symbol.trueName)) return /*println(tree.isUserCreated, iterateParent(tree.symbol), force_add) @@ -427,8 +442,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } def addOccurencePatternTree(tree: Pattern, - type_symbol: s.SymbolOccurrence.Role, - range: s.Range): Unit = { + type_symbol: s.SymbolOccurrence.Role, + range: s.Range): Unit = { if (tree.isUserCreated) { addOccurence(tree.symbol, type_symbol, range) } @@ -501,7 +516,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { iterateParent(path_term.symbol) } case Term.Ident(x) => { - val range_x = range(path_term, path_term.pos, path_term.symbol.name) + val range_x = range(path_term, path_term.pos, path_term.symbol.trueName) addOccurenceTree(path_term, s.SymbolOccurrence.Role.REFERENCE, range_x) @@ -545,7 +560,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case TypeTree.Select(qualifier, _) => { val typetree = extractTypeTree(tree) - val range = rangeSelect(typetree.symbol.name, typetree.pos) + val range = rangeSelect(typetree.symbol.trueName, typetree.pos) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, range) @@ -553,7 +568,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case TypeTree.Inferred() => { val typetree = extractTypeTree(tree) - addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, posToRange(typetree.pos).get) + addOccurenceTypeTree(typetree, + s.SymbolOccurrence.Role.REFERENCE, + posToRange(typetree.pos).get) } case _ => super.traverseTypeTree(tree) @@ -563,7 +580,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { override def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = { tree match { case Pattern.Bind(name, _) => { - addOccurencePatternTree(tree, + addOccurencePatternTree( + tree, s.SymbolOccurrence.Role.REFERENCE, s.Range(tree.symbol.pos.startLine, tree.symbol.pos.startColumn, @@ -583,7 +601,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Import(path, selectors) => - val key = (tree.symbol.name, tree.pos.start) + val key = (tree.symbol.trueName, tree.pos.start) if (!package_definitions(key)) { package_definitions += key getImportSelectors(getImportPath(path), selectors) @@ -596,67 +614,68 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case ClassDef(classname, constr, parents, selfopt, statements) => { - // we first add the class to the symbol list - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.name)) - //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) - //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) - // then the constructor - if (!constr.isUserCreated) { - fittedInitClassRange = Some( - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1, - tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1)) - } else { - fittedInitClassRange = Some( - s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) + // we first add the class to the symbol list + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + range(tree, tree.symbol.pos, tree.symbol.trueName)) + //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) + //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) + // then the constructor + if (!constr.isUserCreated) { + fittedInitClassRange = Some( + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1, + tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length + 1)) + } else { + fittedInitClassRange = Some( + s.Range(constr.symbol.pos.startLine, + constr.symbol.pos.startColumn, + constr.symbol.pos.endLine, + constr.symbol.pos.endColumn)) + } + traverseTree(constr) + fittedInitClassRange = None + + // we add the parents to the symbol list + forceAddBecauseParents = true + parents.foreach(_ match { + case IsTypeTree(t) => traverseTypeTree(t) + case IsTerm(t) => { + traverseTree(t) } - traverseTree(constr) - fittedInitClassRange = None - - // we add the parents to the symbol list - forceAddBecauseParents = true - parents.foreach(_ match { - case IsTypeTree(t) => traverseTypeTree(t) - case IsTerm(t) => { - traverseTree(t) - } - }) - forceAddBecauseParents = false - - selfopt match { - case Some(vdef @ ValDef(name, _, _)) if name != "_" => { - // To find the current position, we will heuristically - // reparse the source code. - // The process is done in three steps: - // 1) Find a position before the '{' of the self but after any - // non related '{'. Here, it will be the largest end pos of a parent - // 2) Find the first '{' - // 3) Iterate until the character we are seeing is a letter - val startPosSearch: Int = parents.foldLeft( - tree.pos.endColumn)((old: Int, ct: TermOrTypeTree) => + }) + forceAddBecauseParents = false + + selfopt match { + case Some(vdef @ ValDef(name, _, _)) if name != "_" => { + // To find the current position, we will heuristically + // reparse the source code. + // The process is done in three steps: + // 1) Find a position before the '{' of the self but after any + // non related '{'. Here, it will be the largest end pos of a parent + // 2) Find the first '{' + // 3) Iterate until the character we are seeing is a letter + val startPosSearch: Int = parents.foldLeft(tree.pos.end)( + (old: Int, ct: TermOrTypeTree) => ct match { - case IsTerm(t) if t.pos.endColumn < old => t.pos.endColumn + case IsTerm(t) if t.pos.end < old => t.pos.end case _ => old }) - var posColumn = sourceCode.indexOf("{", startPosSearch) - while (!sourceCode(posColumn).isLetter && posColumn < sourceCode.length) posColumn += 1 - - addSelfDefinition(name, - s.Range(vdef.pos.startLine, - posColumn, - vdef.pos.endLine, - posColumn + name.length)) - } - case _ => + var posColumn = sourceCode.indexOf("{", if (startPosSearch == tree.pos.end) tree.pos.start else startPosSearch) + + while (posColumn < sourceCode.length && !sourceCode(posColumn).isLetter) posColumn += 1 + + addSelfDefinition(name, + s.Range(0, + posColumn, + 0, + posColumn + name.length)) } + case _ => + } - statements.foreach(traverseTree) + statements.foreach(traverseTree) } case IsDefinition(cdef) => { @@ -671,7 +690,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { s.Range(cdef.pos.startLine, startColumn, cdef.pos.startLine, - startColumn + within.typeSymbol.name.length) + startColumn + within.typeSymbol.trueName.length) ) } case _ => @@ -686,18 +705,18 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { s.Range(cdef.pos.startLine, startColumn, cdef.pos.startLine, - startColumn + within.typeSymbol.name.length) + startColumn + within.typeSymbol.trueName.length) ) } case _ => } } - if (tree.symbol.name != "") { - val range_symbol = range(tree, tree.symbol.pos, tree.symbol.name) - //println(tree, tree.symbol.name, tree.symbol.owner, tree.symbol.owner.flags) - if (tree.symbol.name == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { - //println("omitting", tree.symbol.name) - } else if (tree.symbol.name == "" && fittedInitClassRange != None) { + if (tree.symbol.trueName != "") { + val range_symbol = range(tree, tree.symbol.pos, tree.symbol.trueName) + //println(tree, tree.symbol.trueName, tree.symbol.owner, tree.symbol.owner.flags) + if (tree.symbol.trueName == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { + //println("omitting", tree.symbol.trueName) + } else if (tree.symbol.trueName == "" && fittedInitClassRange != None) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, fittedInitClassRange.get, @@ -718,34 +737,37 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Select(qualifier, _) => { val range = { - val r = rangeSelect(tree.symbol.name, tree.pos) - if (tree.symbol.name == "") + println("") + + +println(tree.symbol.trueName) + println("") + val r = rangeSelect(tree.symbol.trueName, tree.pos) + if (tree.symbol.trueName == "") s.Range(r.startLine, r.startCharacter + 1, r.endLine, r.endCharacter + 1) else r } - addOccurenceTree(tree, - s.SymbolOccurrence.Role.REFERENCE, - range) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTree(tree) } case Term.Ident(name) => { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.name)) + range(tree, tree.pos, tree.symbol.trueName)) super.traverseTree(tree) } case PackageClause(_) => - val key = (tree.symbol.name, tree.pos.start) + val key = (tree.symbol.trueName, tree.pos.start) if (!package_definitions(key)) { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.name)) + range(tree, tree.pos, tree.symbol.trueName)) package_definitions += key } super.traverseTree(tree) diff --git a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala index 225d375bd5b2..d3617c601849 100644 --- a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala +++ b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala @@ -87,7 +87,6 @@ object Semanticdbs { occurrences.foreach { occ => val range = occ.range.get val end = sourceFile.lineToOffset(range.endLine) + range.endCharacter - //println(occ) sb.append(doc.text.substring(offset, end)) sb.append(" /* ") .append(occ.symbol) @@ -118,7 +117,14 @@ object Semanticdbs { a.startCharacter, b.startCharacter ) - byCharacter + if (byCharacter == 0) { + Integer.compare( + a.endCharacter, + b.endCharacter + ) + } else { + byCharacter + } } } } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 4da0f603b649..a4238fd5a1f3 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -175,36 +175,38 @@ class Tests { } - //@Test def testAccess(): Unit = checkFile("example/Access.scala") - //@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") - //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - //@Test def testClasses(): Unit = checkFile("example/Classes.scala") - //@Test def testEmpty(): Unit = checkFile("example/Empty.scala") - //@Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") - //@Test def testExample(): Unit = checkFile("example/Example.scala") - //@Test def testExample2(): Unit = checkFile("example/Example2.scala") - //@Test def testExclude(): Unit = checkFile("example/Exclude.scala") - //WIP @Test def testFlags(): Unit = checkFile("example/Flags.scala") - //@Test def testImports(): Unit = checkFile("example/Imports.scala") - //@Test def testIssue1749(): Unit = checkFile("example/Issue1749.scala") - //@Test def testLocalFile(): Unit = checkFile("example/local-file.scala") - //@Test def testLocals(): Unit = checkFile("example/Locals.scala") - //@Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") - //WIP(assert) @Test def testMethods(): Unit = checkFile("example/Methods.scala") - //@Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") - //@Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") - //def testObjects(): Unit = checkFile("example/Objects.scala") - //@Test def testOverrides(): Unit = checkFile("example/Overrides.scala") + /*@Test def testAccess(): Unit = checkFile("example/Access.scala") + @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") + @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + @Test def testClasses(): Unit = checkFile("example/Classes.scala") + @Test def testEmpty(): Unit = checkFile("example/Empty.scala") + @Test def testEmptyObject(): Unit = checkFile("example/EmptyObject.scala") + @Test def testExample(): Unit = checkFile("example/Example.scala") + @Test def testExample2(): Unit = checkFile("example/Example2.scala") + @Test def testExclude(): Unit = checkFile("example/Exclude.scala") + @Test def testFlags(): Unit = checkFile("example/Flags.scala") + @Test def testImports(): Unit = checkFile("example/Imports.scala") + @Test def testIssue1749(): Unit = checkFile("example/Issue1749.scala") + @Test def testLocalFile(): Unit = checkFile("example/local-file.scala") + @Test def testLocals(): Unit = checkFile("example/Locals.scala") + @Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") + @Test def testMethods(): Unit = checkFile("example/Methods.scala") + @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + @Test def testObjects(): Unit = checkFile("example/Objects.scala") + @Test def testOverrides(): Unit = checkFile("example/Overrides.scala") @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") - //@Test def testSelfs(): Unit = checkFile("example/Selfs.scala") - //@Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") + @Test def testSelfs(): Unit = checkFile("example/Selfs.scala") + @Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") + @Test def testTraits(): Unit = checkFile("example/Traits.scala") + @Test def testTypes(): Unit = checkFile("example/Types.scala") + @Test def testVals(): Unit = checkFile("example/Vals.scala") + @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") + @Test def testNew(): Unit = checkFile("example/New.scala") + @Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") + @Test def testCase(): Unit = checkFile("example/Case.scala") + @Test def testApply(): Unit = checkFile("example/Apply.scala") + @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") - //WIP @Test def testTraits(): Unit = checkFile("example/Traits.scala") - //@Test def testTypes(): Unit = checkFile("example/Types.scala") - //@Test def testVals(): Unit = checkFile("example/Vals.scala") - //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") - //@Test def testNew(): Unit = checkFile("example/New.scala") - //@Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") - //@Test def testCase(): Unit = checkFile("example/Case.scala") - //@Test def testApply(): Unit = checkFile("example/Apply.scala") + //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") + @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") } From 51607632777c2dcb59bbd166dae26fc925dbcf6a Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 11 Dec 2018 21:03:48 +0100 Subject: [PATCH 18/42] improve 'isUserCreated' for typetrees --- .../input/src/main/scala/example/BinaryOp.scala | 2 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 17 +++++++++++++++-- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/BinaryOp.scala b/semanticdb/input/src/main/scala/example/BinaryOp.scala index 8a67f508c0f7..20d6a6187dd6 100644 --- a/semanticdb/input/src/main/scala/example/BinaryOp.scala +++ b/semanticdb/input/src/main/scala/example/BinaryOp.scala @@ -1,5 +1,5 @@ package example class BinaryOp { - 1 #:: 2 #:: Stream.empty + 1 #:: 2 #:: Stream.empty[Int] } \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 7c9dac33b617..db3aa6d94ecf 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -35,12 +35,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { object ChildTraverser extends TreeTraverser { var children: List[Tree] = Nil + var childrenType: List[TypeOrBoundsTree] = 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 = () + implicit ctx: Context): Unit = + childrenType = tree :: childrenType override def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = () override def traverseTypeCaseDef(tree: TypeCaseDef)( @@ -52,6 +54,11 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { traverseTreeChildren(tree)(ctx) return children } + def getChildrenType(tree: TypeOrBoundsTree)(implicit ctx: Context): List[TypeOrBoundsTree] = { + childrenType = Nil + traverseTypeTreeChildren(tree)(ctx) + return childrenType + } } object Traverser extends TreeTraverser { @@ -66,6 +73,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { implicit class TypeTreeExtender(tree: TypeTree) { def isUserCreated: Boolean = { + val children: List[Position] = + ChildTraverser.getChildrenType(tree)(reflect.rootContext).collect(_ match { + case IsTypeTree(tt) => tt.pos}) + println(children) + return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children + .exists(_ == tree.pos)) return !(tree.pos.exists && tree.pos.start == tree.pos.end) } } @@ -436,7 +449,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - println(typetree) + println(typetree.symbol, typetree.isUserCreated) if (typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index a4238fd5a1f3..440803021963 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -175,7 +175,7 @@ class Tests { } - /*@Test def testAccess(): Unit = checkFile("example/Access.scala") + /*@Test def testAccess(): Unit = checkFile("example/Access.scala") @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") @Test def testClasses(): Unit = checkFile("example/Classes.scala") @@ -205,8 +205,9 @@ class Tests { @Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") @Test def testCase(): Unit = checkFile("example/Case.scala") @Test def testApply(): Unit = checkFile("example/Apply.scala") + @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ + @Test def testTraits(): Unit = checkFile("example/Traits.scala") //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") - @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") } From 051c726f5a08496ee1646ed7afbb206ed3318573 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 11 Dec 2018 21:39:34 +0100 Subject: [PATCH 19/42] clean up --- semanticdb/src/dotty/semanticdb/Main.scala | 3 ++- .../dotty/semanticdb/SemanticdbConsumer.scala | 23 ------------------- semanticdb/test/dotty/semanticdb/Tests.scala | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/Main.scala b/semanticdb/src/dotty/semanticdb/Main.scala index 9fe05a8c1572..17d83333b875 100644 --- a/semanticdb/src/dotty/semanticdb/Main.scala +++ b/semanticdb/src/dotty/semanticdb/Main.scala @@ -2,6 +2,7 @@ package dotty.semanticdb import scala.tasty.Reflection import scala.tasty.file._ +import scala.NotImplementedError object Main { def main(args: Array[String]): Unit = { @@ -11,7 +12,7 @@ object Main { println("Dotty Semantic DB: No classes where passed as argument") } else { println("Running Dotty Semantic DB on: " + args.mkString(" ")) - ConsumeTasty(extraClasspath, classes, new SemanticdbConsumer) + //ConsumeTasty(extraClasspath, classes, new SemanticdbConsumer()) } } } diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index db3aa6d94ecf..3c4c936c3685 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -76,10 +76,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val children: List[Position] = ChildTraverser.getChildrenType(tree)(reflect.rootContext).collect(_ match { case IsTypeTree(tt) => tt.pos}) - println(children) return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children .exists(_ == tree.pos)) - return !(tree.pos.exists && tree.pos.start == tree.pos.end) } } @@ -160,7 +158,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isSyntheticConstructor(implicit ctx: Context): Boolean = { val isObjectConstructor = symbol.isConstructor && symbol.owner != NoSymbol && symbol.owner.flags.is(Flags.Object) - //println("====>", symbol, symbol.owner, symbol.owner.flags, symbol.owner.flags.isObject, isObjectConstructor) val isModuleConstructor = symbol.isConstructor && symbol.owner.isClass val isTraitConstructor = symbol.isConstructor && symbol.owner.isTrait val isInterfaceConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.isTrait @@ -431,16 +428,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reservedFunctions .contains(tree.symbol.trueName)) return - /*println(tree.isUserCreated, iterateParent(tree.symbol), force_add) - - val children: List[Position] = - ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) - println("#####", tree.pos.start, tree.pos.end) - if (tree.symbol.pos.exists) { - println("#####", tree.symbol.pos.start, tree.symbol.pos.end, tree, tree.symbol.name) - - } - children.foreach(p => println(p.start, p.end))*/ if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( tree.symbol) == "java/lang/Object#``()."))) { addOccurence(tree.symbol, type_symbol, range) @@ -449,7 +436,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - println(typetree.symbol, typetree.isUserCreated) if (typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } @@ -631,8 +617,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, range(tree, tree.symbol.pos, tree.symbol.trueName)) - //println("constr symbol pos: ", constr.symbol.pos.startColumn, constr.symbol.pos.endColumn) - //println("constr pos: ", constr.pos.startColumn, constr.pos.endColumn) // then the constructor if (!constr.isUserCreated) { fittedInitClassRange = Some( @@ -726,9 +710,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } if (tree.symbol.trueName != "") { val range_symbol = range(tree, tree.symbol.pos, tree.symbol.trueName) - //println(tree, tree.symbol.trueName, tree.symbol.owner, tree.symbol.owner.flags) if (tree.symbol.trueName == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { - //println("omitting", tree.symbol.trueName) } else if (tree.symbol.trueName == "" && fittedInitClassRange != None) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -750,11 +732,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Select(qualifier, _) => { val range = { - println("") - - -println(tree.symbol.trueName) - println("") val r = rangeSelect(tree.symbol.trueName, tree.pos) if (tree.symbol.trueName == "") s.Range(r.startLine, diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 440803021963..1638acdc2b84 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -207,7 +207,7 @@ class Tests { @Test def testApply(): Unit = checkFile("example/Apply.scala") @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ - @Test def testTraits(): Unit = checkFile("example/Traits.scala") + //@Test def testTraits(): Unit = checkFile("example/Traits.scala") //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") } From db2e922ab5fe265f33623ea1cb624df06af493fc Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 12 Dec 2018 00:32:47 +0100 Subject: [PATCH 20/42] Support locally, implicitly. Assert are experimental --- .../src/main/scala/example/Anonymous.scala | 8 +-- .../src/main/scala/example/DottyPredef.scala | 9 +++ .../src/main/scala/example/MethodUsages.scala | 6 +- .../input/src/main/scala/example/Super.scala | 9 +++ .../dotty/semanticdb/SemanticdbConsumer.scala | 69 +++++++++++++++++-- semanticdb/test/dotty/semanticdb/Tests.scala | 5 +- 6 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/DottyPredef.scala create mode 100644 semanticdb/input/src/main/scala/example/Super.scala diff --git a/semanticdb/input/src/main/scala/example/Anonymous.scala b/semanticdb/input/src/main/scala/example/Anonymous.scala index 83317b3c06db..e807429f536c 100644 --- a/semanticdb/input/src/main/scala/example/Anonymous.scala +++ b/semanticdb/input/src/main/scala/example/Anonymous.scala @@ -4,15 +4,15 @@ import scala.language.higherKinds class Anonymous { this: Anonymous => - /*def m1[T[_], B] = ??? + def m1[T[_], B] = ??? def m2: Map[_, List[_]] = ??? locally { ??? match { case _: List[_] => } - }*/ + } locally { val x: Int => Int = _ => ??? } - /* + trait Foo - var x = new Foo {}*/ + var x = new Foo {} } \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/DottyPredef.scala b/semanticdb/input/src/main/scala/example/DottyPredef.scala new file mode 100644 index 000000000000..4aad3f07ef77 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/DottyPredef.scala @@ -0,0 +1,9 @@ +package example + +class PredefsDotty { + locally { + val x: Int => Int = _ => ??? + } + //assert(true) + //assert(false, "bonjour") +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/MethodUsages.scala b/semanticdb/input/src/main/scala/example/MethodUsages.scala index bcec38de7d3f..ea0b9e93713f 100644 --- a/semanticdb/input/src/main/scala/example/MethodUsages.scala +++ b/semanticdb/input/src/main/scala/example/MethodUsages.scala @@ -2,7 +2,7 @@ package example class MethodUsages { val m = new Methods[Int] - /*m.m1 + m.m1 m.m2() m.m3(0) m.m4(0)(0) @@ -13,7 +13,7 @@ class MethodUsages { m.m6(Nil) m.m7a(m, new m.List[Int]) m.m7b(new m.List[Int]) - */m.`m8().`()/* + m.`m8().`() m.m9(null) m.m10(null) m.m11(Predef) @@ -29,5 +29,5 @@ class MethodUsages { m.m17("") m.m18.m() m.m18(1) - m.m18("")*/ + m.m18("") } diff --git a/semanticdb/input/src/main/scala/example/Super.scala b/semanticdb/input/src/main/scala/example/Super.scala new file mode 100644 index 000000000000..c052b9113769 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/Super.scala @@ -0,0 +1,9 @@ +package example + +class SuperA { + def a(x:Int) = x +} + +class SuperB extends SuperA { + override def a(x:Int) = super.a(x) +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 3c4c936c3685..6f844b296d94 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -66,6 +66,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isUserCreated: Boolean = { val children: List[Position] = ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) + /*println("call to isusercreated on " + iterateParent(tree.symbol)) + if (tree.pos.exists) + println(tree.pos.start, tree.pos.end) + println(children.map(x => (x.start, x.end)))*/ return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children .exists(_ == tree.pos)) } @@ -251,9 +255,16 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbol.name != tpnme.THIS.toString } + def isAnonymousInit(implicit ctx: Context): Boolean = { + return symbol.owner != NoSymbol && + (symbol.owner.isAnonymousFunction || symbol.owner.isAnonymousClass) && + symbol.name == "" + } + def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || //symbol.isInitChild || + symbol.isAnonymousInit || symbol.isDefaultGetter || symbol.isWildCard || symbol.isAnonymousClass || @@ -353,6 +364,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + def addOccurencePredef(parent: String, name: String, range: s.Range): Unit = { + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + parent + name + "().", + s.SymbolOccurrence.Role.DEFINITION + ) + } def addSelfDefinition(name: String, range: s.Range): Unit = { var localsymbol = Symbols.Local(local_offset.toString) local_offset += 1 @@ -393,6 +413,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => symbolToSymbolString(symbol) } + // We want to add symbols coming from our file + // if (symbol.pos.sourceFile != sourceFile) return if (symbol_path == "" || symbol.isUselessOccurrence) return val key = (symbol_path, range) @@ -436,6 +458,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { + println(typetree.isUserCreated) if (typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } @@ -571,8 +594,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { s.SymbolOccurrence.Role.REFERENCE, posToRange(typetree.pos).get) } - case _ => + case _ => { super.traverseTypeTree(tree) + } } } @@ -597,6 +621,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { var fittedInitClassRange: Option[s.Range] = None var forceAddBecauseParents: Boolean = false + def getNumberParametersInit(defdef: DefDef)(implicit ctx: Context): Int = { + defdef match { + case DefDef(_, typeParams, paramss, _, _) => + paramss.foldLeft(0)((old, c) => old + c.length) + typeParams.length + case _ => 0 + } + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Import(path, selectors) => @@ -645,7 +677,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { forceAddBecauseParents = false selfopt match { - case Some(vdef @ ValDef(name, _, _)) if name != "_" => { + case Some(vdef @ ValDef(name, type_, _)) if name != "_" => { // To find the current position, we will heuristically // reparse the source code. // The process is done in three steps: @@ -668,11 +700,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { posColumn, 0, posColumn + name.length)) + println(type_) + traverseTypeTree(type_) } case _ => } - statements.foreach(traverseTree) + statements.takeRight(statements.length - getNumberParametersInit(constr)).foreach(traverseTree) } case IsDefinition(cdef) => { @@ -740,7 +774,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { r.endCharacter + 1) else r } - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, forceAddBecauseParents) super.traverseTree(tree) } @@ -752,6 +786,33 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(tree) } + case Term.Inlined(Some(c), b, d) => { + def extractPos(x: TermOrTypeTree) = x match { + case IsTerm(t) => t.pos + case IsTypeTree(t) => t.pos + } + def extractSymbol(x: TermOrTypeTree) = x match { + case IsTerm(t) => t.symbol + case IsTypeTree(t) => t.symbol + } + def getPredefFunction(pos: Int): String = { + sourceCode(pos) match { + case 'l' => "locally" + case 'i' => "implicitly" + case 'a' => "assert" + } + } + val parentSymbol = iterateParent(extractSymbol(c)) + if (parentSymbol == "dotty/DottyPredef.") { + val pos = extractPos(c) + val function = getPredefFunction(pos.start) + val range = s.Range(pos.startLine, pos.startColumn, pos.startLine, pos.startColumn + function.length) + addOccurencePredef(parentSymbol, function, range) + } + + super.traverseTree(tree) + } + case PackageClause(_) => val key = (tree.symbol.trueName, tree.pos.start) if (!package_definitions(key)) { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 1638acdc2b84..4d072175844d 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -206,8 +206,11 @@ class Tests { @Test def testCase(): Unit = checkFile("example/Case.scala") @Test def testApply(): Unit = checkFile("example/Apply.scala") @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") + @Test def testSuper(): Unit = checkFile("example/Super.scala") @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ //@Test def testTraits(): Unit = checkFile("example/Traits.scala") //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") -} + //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") + } From f11c8ffde3c127a308a59ac1b7c871e68cc2191a Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 26 Dec 2018 18:25:00 +0100 Subject: [PATCH 21/42] do not register compiler generated definitions --- semanticdb/input/src/main/scala/example/Case.scala | 6 +++--- semanticdb/input/src/main/scala/example/Example.scala | 4 ++-- .../src/dotty/semanticdb/SemanticdbConsumer.scala | 11 ++++++++--- semanticdb/test/dotty/semanticdb/Tests.scala | 5 ++++- vscode-dotty/package-lock.json | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Case.scala b/semanticdb/input/src/main/scala/example/Case.scala index 6aebcb750088..149d57dc27de 100644 --- a/semanticdb/input/src/main/scala/example/Case.scala +++ b/semanticdb/input/src/main/scala/example/Case.scala @@ -1,5 +1,5 @@ package example - +/* class CaseTest { def foo (x: Option[Int]) : Int = x match { @@ -7,6 +7,6 @@ class CaseTest { case None => 0 } } - +*/ case class CaseClass(x: Int) -case object CaseObject \ No newline at end of file +//case object CaseObject \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index 4e431eccf47a..6d6dc202f941 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -15,7 +15,7 @@ class Example { y ) } - +/* class ExampleInit { -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 6f844b296d94..89ade3e479e5 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -416,6 +416,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { // We want to add symbols coming from our file // if (symbol.pos.sourceFile != sourceFile) return if (symbol_path == "" || symbol.isUselessOccurrence) return + if (symbol.flags.is(Flags.Synthetic) && type_symbol == s.SymbolOccurrence.Role.DEFINITION) return val key = (symbol_path, range) // TODO: refactor the following @@ -430,7 +431,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } println(symbol_path, range, - symbol.owner.flags, + symbol.flags, is_global, iterateParent(symbol)) occurrences = @@ -583,17 +584,18 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case TypeTree.Select(qualifier, _) => { val typetree = extractTypeTree(tree) val range = rangeSelect(typetree.symbol.trueName, typetree.pos) + println(typetree.pos.start, typetree.pos.end) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTypeTree(typetree) - } + }/* case TypeTree.Inferred() => { val typetree = extractTypeTree(tree) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, posToRange(typetree.pos).get) - } + }*/ case _ => { super.traverseTypeTree(tree) } @@ -829,7 +831,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + println("{--------------------------------------}") println(root) + println("{--------------------------------------}") + Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 4d072175844d..37f954d06519 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -212,5 +212,8 @@ class Tests { //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") + //@Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") + @Test def testCase(): Unit = checkFile("example/Case.scala") + + } diff --git a/vscode-dotty/package-lock.json b/vscode-dotty/package-lock.json index 65f388b9ef52..e9e6ca8c5b82 100644 --- a/vscode-dotty/package-lock.json +++ b/vscode-dotty/package-lock.json @@ -27,7 +27,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, @@ -296,7 +296,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "requires": { "readable-stream": "^2.3.5", From 863cf1d4b97972c3b3142d1cfa72a7edd82fd674 Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 27 Dec 2018 19:00:13 +0100 Subject: [PATCH 22/42] fix case class definitions --- semanticdb/input/src/main/scala/example/Case.scala | 6 +++--- semanticdb/input/src/main/scala/example/Example.scala | 4 ++-- semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala | 2 +- semanticdb/test/dotty/semanticdb/Tests.scala | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Case.scala b/semanticdb/input/src/main/scala/example/Case.scala index 149d57dc27de..6aebcb750088 100644 --- a/semanticdb/input/src/main/scala/example/Case.scala +++ b/semanticdb/input/src/main/scala/example/Case.scala @@ -1,5 +1,5 @@ package example -/* + class CaseTest { def foo (x: Option[Int]) : Int = x match { @@ -7,6 +7,6 @@ class CaseTest { case None => 0 } } -*/ + case class CaseClass(x: Int) -//case object CaseObject \ No newline at end of file +case object CaseObject \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index 6d6dc202f941..4e431eccf47a 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -15,7 +15,7 @@ class Example { y ) } -/* + class ExampleInit { -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 89ade3e479e5..73b4df78505f 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -669,7 +669,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { fittedInitClassRange = None // we add the parents to the symbol list - forceAddBecauseParents = true + forceAddBecauseParents = !(tree.symbol.flags.is(Flags.Case)) parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) case IsTerm(t) => { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 37f954d06519..1eceb77b31c9 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -214,6 +214,7 @@ class Tests { //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") //@Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") @Test def testCase(): Unit = checkFile("example/Case.scala") + @Test def teastCase(): Unit = checkFile("example/Example.scala") } From 6a5d6f081a9a52fa61fc37033a92404f706c6ddb Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 10 Jan 2019 16:43:00 +0100 Subject: [PATCH 23/42] Compile semanticdb tests using -Yno-inline --- project/Build.scala | 1 + semanticdb/input/src/main/scala/example/DottyPredef.scala | 4 ++-- semanticdb/test/dotty/semanticdb/Tests.scala | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 7bbc21f1f01c..3c82b9669d98 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -378,6 +378,7 @@ object Build { baseDirectory in (Compile, run) := baseDirectory.value / "..", baseDirectory in Test := baseDirectory.value / "..", unmanagedSourceDirectories in Test += baseDirectory.value / "input" / "src" / "main" / "scala", + scalacOptions in Test ++= Seq("-Yno-inline"), libraryDependencies ++= List( ("org.scalameta" %% "semanticdb" % "4.0.0").withDottyCompat(scalaVersion.value), "com.novocode" % "junit-interface" % "0.11", diff --git a/semanticdb/input/src/main/scala/example/DottyPredef.scala b/semanticdb/input/src/main/scala/example/DottyPredef.scala index 4aad3f07ef77..4f9215283fc2 100644 --- a/semanticdb/input/src/main/scala/example/DottyPredef.scala +++ b/semanticdb/input/src/main/scala/example/DottyPredef.scala @@ -4,6 +4,6 @@ class PredefsDotty { locally { val x: Int => Int = _ => ??? } - //assert(true) - //assert(false, "bonjour") + assert(true) + assert(false, "bonjour") } \ No newline at end of file diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 1eceb77b31c9..e9a385537ae5 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -212,9 +212,7 @@ class Tests { //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") - //@Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") + @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") @Test def testCase(): Unit = checkFile("example/Case.scala") - @Test def teastCase(): Unit = checkFile("example/Example.scala") - } From c81c79c96fcfaa35340df317aa76ac1e3df1a2d7 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sat, 12 Jan 2019 16:10:15 +0100 Subject: [PATCH 24/42] Add basic cli for semanticdb --- semanticdb/src/dotty/semanticdb/Main.scala | 45 ++++++++++++++++++++ semanticdb/test/dotty/semanticdb/Tests.scala | 3 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/semanticdb/src/dotty/semanticdb/Main.scala b/semanticdb/src/dotty/semanticdb/Main.scala index 17d83333b875..1df6ef23ae1d 100644 --- a/semanticdb/src/dotty/semanticdb/Main.scala +++ b/semanticdb/src/dotty/semanticdb/Main.scala @@ -4,10 +4,55 @@ import scala.tasty.Reflection import scala.tasty.file._ import scala.NotImplementedError +import dotty.tools.dotc.Driver + object Main { + def parseArguments(args: Array[String]): (Map[String, String], Boolean) = { + def nextArgument(optionMap: Map[String, String], args: List[String]): Map[String, String] = args match { + case "--out" :: file :: tail => nextArgument(optionMap + ("outfile" -> file), tail) + case "-o" :: file :: tail => nextArgument(optionMap + ("outfile" -> file), tail) + case "--help" :: tail => nextArgument(optionMap + ("help" -> ""), tail) + case "-h" :: tail => nextArgument(optionMap + ("help" -> ""), tail) + case "--temp" :: folder :: tail => nextArgument(optionMap + ("temp" -> folder), tail) + case "-t" :: folder :: tail => nextArgument(optionMap + ("temp" -> folder), tail) + case file :: tail => nextArgument(optionMap + ("input" -> file), tail) + case Nil => optionMap + } + + val help = """Usage semanticdb [options] [file] + |Generate semanticdb's information related to the source file [file] + |Options are: + | -h,--help Show help + | -o , --out Place the output into + | -t , --temp Use as the temp directory to store build artifacts + """.stripMargin + + var optionMap = nextArgument(Map(), args.toList) + if (optionMap.contains("help") || !optionMap.contains("input")) { + println(help) + return (optionMap, false) + } else { + if (!optionMap.contains("temp")) { + optionMap += ("temp" -> "/home/pierre/epfl/semester/dotty/semanticdb/testClass/") + } + val driver = new Driver + val compilerParams : List[String] = + optionMap("input") :: + "-Yno-inline" :: + "-d" :: optionMap("temp") :: + "-classpath" :: "/home/pierre/epfl/semester/dotty/library/../out/bootstrap/dotty-library-bootstrapped/scala-0.12/dotty-library_0.12-0.12.0-bin-SNAPSHOT.jar" :: + Nil + + val foo = driver.process(compilerParams.toArray) + return (optionMap, true) + } + } + + def main(args: Array[String]): Unit = { val extraClasspath = "." // TODO allow to set it from the args with -classpath XYZ val classes = args.toList + val (optionMap, canTreat) = parseArguments(args) if (args.isEmpty) { println("Dotty Semantic DB: No classes where passed as argument") } else { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index e9a385537ae5..fcd4703a56f3 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -209,10 +209,11 @@ class Tests { @Test def testSuper(): Unit = checkFile("example/Super.scala") @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ //@Test def testTraits(): Unit = checkFile("example/Traits.scala") - //@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") + @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") @Test def testCase(): Unit = checkFile("example/Case.scala") + } From 6361419339554939b54a8d93ce80d3f79fd2602a Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 14 Jan 2019 00:44:32 +0100 Subject: [PATCH 25/42] Make semanticdb cli able to generate a semanticdb file for a given scala file --- semanticdb/src/dotty/semanticdb/Main.scala | 116 ++++++++++++------ .../dotty/semanticdb/SemanticdbConsumer.scala | 4 +- .../semanticdb/TastyScalaFileInferrer.scala | 49 ++++++++ semanticdb/src/dotty/semanticdb/Utils.scala | 80 ++++++++++++ semanticdb/test/dotty/semanticdb/Tests.scala | 2 +- 5 files changed, 210 insertions(+), 41 deletions(-) create mode 100644 semanticdb/src/dotty/semanticdb/TastyScalaFileInferrer.scala create mode 100644 semanticdb/src/dotty/semanticdb/Utils.scala diff --git a/semanticdb/src/dotty/semanticdb/Main.scala b/semanticdb/src/dotty/semanticdb/Main.scala index 1df6ef23ae1d..522d6ad42a91 100644 --- a/semanticdb/src/dotty/semanticdb/Main.scala +++ b/semanticdb/src/dotty/semanticdb/Main.scala @@ -5,59 +5,99 @@ import scala.tasty.file._ import scala.NotImplementedError import dotty.tools.dotc.Driver +import dotty.tools.dotc.reporting.Reporter + +import java.io.File +import java.nio.file._ object Main { - def parseArguments(args: Array[String]): (Map[String, String], Boolean) = { - def nextArgument(optionMap: Map[String, String], args: List[String]): Map[String, String] = args match { - case "--out" :: file :: tail => nextArgument(optionMap + ("outfile" -> file), tail) - case "-o" :: file :: tail => nextArgument(optionMap + ("outfile" -> file), tail) + val userHome = System.getProperty("user.home") + val classpaths = + userHome + "/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.8.jar" :: + "out/bootstrap/dotty-library-bootstrapped/scala-0.12/dotty-library_0.12-0.12.0-bin-SNAPSHOT.jar" :: Nil + + val help = """Usage semanticdb [options] [file] + |Generate semanticdb's information related to the source file [file] + |Options are: + | -h,--help Show help + | -o , --out Place the output into (default: out.semanticdb) + | -t , --temp Use as the temp directory to store build artifacts + """.stripMargin + + type CliArgs = Map[String, String] + + def parseArguments(args: Array[String]): Option[CliArgs] = { + val optRegex = "$-.*".r + def nextArgument(optionMap: CliArgs, args: List[String]): Option[CliArgs] = args match { + case "--out" :: file :: tail => nextArgument(optionMap + ("out" -> file), tail) + case "-o" :: file :: tail => nextArgument(optionMap + ("out" -> file), tail) case "--help" :: tail => nextArgument(optionMap + ("help" -> ""), tail) case "-h" :: tail => nextArgument(optionMap + ("help" -> ""), tail) - case "--temp" :: folder :: tail => nextArgument(optionMap + ("temp" -> folder), tail) - case "-t" :: folder :: tail => nextArgument(optionMap + ("temp" -> folder), tail) + case "--classpath" :: folder :: tail => nextArgument(optionMap + ("classpath" -> folder), tail) + case "-c" :: folder :: tail => nextArgument(optionMap + ("classpath" -> folder), tail) + case optRegex(_) :: _=> None case file :: tail => nextArgument(optionMap + ("input" -> file), tail) - case Nil => optionMap + case Nil => Some(optionMap) } - val help = """Usage semanticdb [options] [file] - |Generate semanticdb's information related to the source file [file] - |Options are: - | -h,--help Show help - | -o , --out Place the output into - | -t , --temp Use as the temp directory to store build artifacts - """.stripMargin - - var optionMap = nextArgument(Map(), args.toList) - if (optionMap.contains("help") || !optionMap.contains("input")) { - println(help) - return (optionMap, false) - } else { - if (!optionMap.contains("temp")) { - optionMap += ("temp" -> "/home/pierre/epfl/semester/dotty/semanticdb/testClass/") + nextArgument(Map(), args.toList) match { + case Some(args : CliArgs) => { + var cleanedArgs = args + cleanedArgs += "out" -> cleanedArgs.getOrElse("out", "out.semanticdb") + if (cleanedArgs.contains("help") || !cleanedArgs.contains("input")) { + return None + } else { + cleanedArgs += "classpath" -> cleanedArgs.getOrElse("classpath", Files.createTempDirectory("semanticdb").toString) + val tempFolder = new File(cleanedArgs("classpath")); + if (!tempFolder.exists()){ + tempFolder.mkdir(); + } + return Some(cleanedArgs) } - val driver = new Driver - val compilerParams : List[String] = - optionMap("input") :: - "-Yno-inline" :: - "-d" :: optionMap("temp") :: - "-classpath" :: "/home/pierre/epfl/semester/dotty/library/../out/bootstrap/dotty-library-bootstrapped/scala-0.12/dotty-library_0.12-0.12.0-bin-SNAPSHOT.jar" :: - Nil - - val foo = driver.process(compilerParams.toArray) - return (optionMap, true) } + case None => None + } + } + + def compile(cliArgs : CliArgs) : Reporter + = { + val driver = new Driver + val compilerParams : List[String] = + "-classpath" :: classpaths.mkString(":") :: + "-Yno-inline" :: + "-d" :: cliArgs("classpath") :: + cliArgs("input") :: + Nil + + val reporter = driver.process(compilerParams.toArray) + return reporter } def main(args: Array[String]): Unit = { val extraClasspath = "." // TODO allow to set it from the args with -classpath XYZ val classes = args.toList - val (optionMap, canTreat) = parseArguments(args) - if (args.isEmpty) { - println("Dotty Semantic DB: No classes where passed as argument") - } else { - println("Running Dotty Semantic DB on: " + args.mkString(" ")) - //ConsumeTasty(extraClasspath, classes, new SemanticdbConsumer()) + + + parseArguments(args) match { + case None => println(help) + case Some(cliArgs) => { + val reporter = compile(cliArgs) + + if (reporter.hasErrors) { + println("Compile error:") + println(reporter) + } else { + val classNames = Utils.getClassNames(cliArgs("classpath"), cliArgs("input")) + val scalaFile = Paths.get(cliArgs("input")).toAbsolutePath + val sdbconsumer = new SemanticdbConsumer(scalaFile) + val _ = ConsumeTasty(cliArgs("classpath"), classNames, sdbconsumer) + val textDocument = sdbconsumer.toSemanticdb() + val os = Files.newOutputStream(Paths.get(cliArgs("out"))) + try textDocument.writeTo(os) + finally os.close() + } + } } } } diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 73b4df78505f..43f1b8e666ad 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -19,8 +19,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) 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) + def toSemanticdb(): s.TextDocument = { + s.TextDocument(text = sourceCode, occurrences = occurrences) } val package_definitions: Set[Tuple2[String, Int]] = Set() val symbolsCache: HashMap[(String, s.Range), String] = HashMap() diff --git a/semanticdb/src/dotty/semanticdb/TastyScalaFileInferrer.scala b/semanticdb/src/dotty/semanticdb/TastyScalaFileInferrer.scala new file mode 100644 index 000000000000..15b13a41404d --- /dev/null +++ b/semanticdb/src/dotty/semanticdb/TastyScalaFileInferrer.scala @@ -0,0 +1,49 @@ +package dotty.semanticdb + +import scala.tasty.Reflection + +import scala.meta.internal.{semanticdb => s} +import scala.tasty.Reflection +import scala.tasty.file.TastyConsumer +import java.lang.reflect.InvocationTargetException + +class TastyScalaFileInferrer extends TastyConsumer { + /* Visitor over a tasty tree. + Aims at finding the scala file from where this tree originated. + */ + + /* If a scala file was found sourcePath is Some(scalaFile), + Otherwise None */ + var sourcePath: 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 + sourcePath = Some(path) + case x => super.traverseTree(tree) + } + true + } + } + case _ => { + // If we already found a sourcePath in this tasty file, + // we abort our search here to avoid spending too much time here + if (sourcePath == None) + super.traverseTree(tree) + else + () + } + } + } + ChildTraverser.traverseTree(root)(reflect.rootContext) + } +} diff --git a/semanticdb/src/dotty/semanticdb/Utils.scala b/semanticdb/src/dotty/semanticdb/Utils.scala new file mode 100644 index 000000000000..d7de38cbdf38 --- /dev/null +++ b/semanticdb/src/dotty/semanticdb/Utils.scala @@ -0,0 +1,80 @@ +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 + +object Utils { + /** 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 = + file.toFile().getName().toString() match { + case pat(name) => name + case _ => "" + } + return (classpath, modulename + "." + sourcename) + } + + /** List all tasty files occuring in the folder f or one of its subfolders */ + def recursiveListFiles(f: File): Array[File] = { + val pattern = ".*\\.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(classPath: Path): HashMap[String, List[Path]] = { + val source_to_tasty: HashMap[String, List[Path]] = HashMap() + val tastyfiles = recursiveListFiles(classPath.toFile()) + recursiveListFiles(classPath.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 TastyScalaFileInferrer + ConsumeTasty(classpath, classname :: Nil, inspecter) + inspecter.sourcePath.foreach( + source => + source_to_tasty += + (source -> (tasty_path + .toPath() :: source_to_tasty.getOrElse(source, Nil)))) + } catch { + case _: InvocationTargetException => println(tasty_path) + } + }) + source_to_tasty + } + + /* + Returns the list of names of class defined inside the scala file [scalaFile] + extracted from the compilation artifacts found in [classPath]. + */ + def getClassNames(classPath: String, scalaFile: String): List[String] = { + val tastyFiles = + getTastyFiles(Paths.get(classPath).toAbsolutePath) + .getOrElse(Paths.get(scalaFile).toAbsolutePath.toString, Nil) + + val tastyClasses = tastyFiles.map(getClasspathClassname) + val (_, classnames) = tastyClasses.unzip + return classnames + } +} \ No newline at end of file diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index fcd4703a56f3..9a521d1f000c 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -132,7 +132,7 @@ class Tests { val sdbconsumer = new SemanticdbConsumer(scalaFile) val _ = ConsumeTasty(classpaths.head, classnames, sdbconsumer) - sdbconsumer.toSemanticdb(scalac.text) + sdbconsumer.toSemanticdb() } } From 5617e3ede3a327f8b5fd1f2e942eda8f9839d210 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 14 Jan 2019 00:45:55 +0100 Subject: [PATCH 26/42] bump scala version in semanticdb tests --- semanticdb/test/dotty/semanticdb/Tests.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 9a521d1f000c..74af91c6437a 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -49,9 +49,9 @@ class TastyInspecter extends TastyConsumer { class Tests { - // TODO: update scala-0.10 on version change (or resolve automatically) + // TODO: update scala-0.13 on version change (or resolve automatically) final def tastyClassDirectory = - "out/bootstrap/dotty-semanticdb/scala-0.11/test-classes" + "out/bootstrap/dotty-semanticdb/scala-0.12/test-classes" val sourceroot = Paths.get("semanticdb", "input").toAbsolutePath val sourceDirectory = sourceroot.resolve("src/main/scala") From 4f2a5ff704afc647db045496c6a7e979bcd9cdcd Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 14 Jan 2019 11:34:14 +0100 Subject: [PATCH 27/42] semanticdb tests use new utils class functions --- semanticdb/src/dotty/semanticdb/Main.scala | 2 +- semanticdb/src/dotty/semanticdb/Utils.scala | 32 ++--- semanticdb/test/dotty/semanticdb/Tests.scala | 121 +++---------------- 3 files changed, 31 insertions(+), 124 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/Main.scala b/semanticdb/src/dotty/semanticdb/Main.scala index 522d6ad42a91..0d6a55329170 100644 --- a/semanticdb/src/dotty/semanticdb/Main.scala +++ b/semanticdb/src/dotty/semanticdb/Main.scala @@ -88,8 +88,8 @@ object Main { println("Compile error:") println(reporter) } else { - val classNames = Utils.getClassNames(cliArgs("classpath"), cliArgs("input")) val scalaFile = Paths.get(cliArgs("input")).toAbsolutePath + val classNames = Utils.getClassNames(Paths.get(cliArgs("classpath")), scalaFile) val sdbconsumer = new SemanticdbConsumer(scalaFile) val _ = ConsumeTasty(cliArgs("classpath"), classNames, sdbconsumer) val textDocument = sdbconsumer.toSemanticdb() diff --git a/semanticdb/src/dotty/semanticdb/Utils.scala b/semanticdb/src/dotty/semanticdb/Utils.scala index d7de38cbdf38..ede0044077e1 100644 --- a/semanticdb/src/dotty/semanticdb/Utils.scala +++ b/semanticdb/src/dotty/semanticdb/Utils.scala @@ -30,23 +30,23 @@ object Utils { } /** List all tasty files occuring in the folder f or one of its subfolders */ - def recursiveListFiles(f: File): Array[File] = { - val pattern = ".*\\.tasty".r + def recursiveListFiles(f: File, prefix : String = ""): Array[File] = { + val pattern = (prefix + ".*\\.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) + tastyfiles ++ folders.flatMap(recursiveListFiles(_, prefix)) } /** Returns a mapping from *.scala file to a list of tasty files. */ - def getTastyFiles(classPath: Path): HashMap[String, List[Path]] = { - val source_to_tasty: HashMap[String, List[Path]] = HashMap() - val tastyfiles = recursiveListFiles(classPath.toFile()) - recursiveListFiles(classPath.toFile()).map(tasty_path => { - val (classpath, classname) = getClasspathClassname(tasty_path.toPath()) + def getTastyFiles(classPath: Path, prefix : String = ""): HashMap[String, List[Path]] = { + val sourceToTasty: HashMap[String, List[Path]] = HashMap() + val tastyfiles = recursiveListFiles(classPath.toFile(), prefix) + recursiveListFiles(classPath.toFile()).map(tastyPath => { + val (classpath, classname) = getClasspathClassname(tastyPath.toPath()) // We add an exception here to avoid crashing if we encountered // a bad tasty file try { @@ -54,24 +54,24 @@ object Utils { ConsumeTasty(classpath, classname :: Nil, inspecter) inspecter.sourcePath.foreach( source => - source_to_tasty += - (source -> (tasty_path - .toPath() :: source_to_tasty.getOrElse(source, Nil)))) + sourceToTasty += + (source -> (tastyPath + .toPath().toAbsolutePath :: sourceToTasty.getOrElse(source, Nil)))) } catch { - case _: InvocationTargetException => println(tasty_path) + case _: InvocationTargetException => println(tastyPath) } }) - source_to_tasty + sourceToTasty } /* Returns the list of names of class defined inside the scala file [scalaFile] extracted from the compilation artifacts found in [classPath]. */ - def getClassNames(classPath: String, scalaFile: String): List[String] = { + def getClassNames(classPath: Path, scalaFile: Path, prefix : String = ""): List[String] = { val tastyFiles = - getTastyFiles(Paths.get(classPath).toAbsolutePath) - .getOrElse(Paths.get(scalaFile).toAbsolutePath.toString, Nil) + getTastyFiles(classPath.toAbsolutePath, prefix) + .getOrElse(scalaFile.toString, Nil) val tastyClasses = tastyFiles.map(getClasspathClassname) val (_, classnames) = tastyClasses.unzip diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 74af91c6437a..5612909ca047 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -14,133 +14,40 @@ 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.13 on version change (or resolve automatically) final def tastyClassDirectory = - "out/bootstrap/dotty-semanticdb/scala-0.12/test-classes" - val sourceroot = Paths.get("semanticdb", "input").toAbsolutePath - val sourceDirectory = sourceroot.resolve("src/main/scala") + Paths.get("out/bootstrap/dotty-semanticdb/scala-0.12/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 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 } - /** 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 = - file.toFile().getName().toString() match { - case pat(name) => name - case _ => "" - } - 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(scalaFile) - - val _ = ConsumeTasty(classpaths.head, classnames, sdbconsumer) - sdbconsumer.toSemanticdb() - } + /** Returns the SemanticDB for this Scala source file. */ + def getTastySemanticdb(classPath: Path, scalaFile: Path) : s.TextDocument = { + val classNames = Utils.getClassNames(classPath, scalaFile, "example/") + println(classPath) + println(classNames) + println(scalaFile) + val sdbconsumer = new SemanticdbConsumer(scalaFile) + + val _ = ConsumeTasty(classPath.toString, classNames, sdbconsumer) + sdbconsumer.toSemanticdb() } /** 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 = getTastySemanticdb(path) + val tasty = getTastySemanticdb(tastyClassDirectory, path) println(tasty) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) From 98ff433ac8fb1a755196f7529c975acab58424f1 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 16 Jan 2019 00:18:01 +0100 Subject: [PATCH 28/42] Bug fixes on types, this overloading and super statement --- .../src/main/scala/example/BinaryOp.scala | 3 +- .../input/src/main/scala/example/Flags.scala | 9 +- .../src/main/scala/example/Imports.scala | 1 - .../main/scala/example/MacroAnnotations.scala | 17 ++- .../input/src/main/scala/example/Types.scala | 13 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 137 +++++++++++++----- semanticdb/test/dotty/semanticdb/Tests.scala | 15 +- 7 files changed, 136 insertions(+), 59 deletions(-) delete mode 100644 semanticdb/input/src/main/scala/example/Imports.scala diff --git a/semanticdb/input/src/main/scala/example/BinaryOp.scala b/semanticdb/input/src/main/scala/example/BinaryOp.scala index 20d6a6187dd6..27929e243aa9 100644 --- a/semanticdb/input/src/main/scala/example/BinaryOp.scala +++ b/semanticdb/input/src/main/scala/example/BinaryOp.scala @@ -1,5 +1,6 @@ package example class BinaryOp { - 1 #:: 2 #:: Stream.empty[Int] + val y = 1 #:: 2 #:: Stream.empty[Int] + val x = 1 + 2 + 3 } \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Flags.scala b/semanticdb/input/src/main/scala/example/Flags.scala index 51c56d2fac47..dd96fa6621f8 100644 --- a/semanticdb/input/src/main/scala/example/Flags.scala +++ b/semanticdb/input/src/main/scala/example/Flags.scala @@ -1,11 +1,10 @@ -package example -package object p { +package object example { private lazy val x = 1 - protected implicit var y: Int = 2 + /*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) { + abstract class Cex[+T, -U, V](x: T, y: U, z: V) { def this() = this(???, ???, ???) def w: Int } @@ -20,5 +19,5 @@ package object p { class S[@specialized T] val List(xs1) = ??? ??? match { case List(xs2) => ??? } - ??? match { case _: List[t] => ??? } + ??? match { case _: List[t] => ??? }*/ } diff --git a/semanticdb/input/src/main/scala/example/Imports.scala b/semanticdb/input/src/main/scala/example/Imports.scala deleted file mode 100644 index 5f4ead1f23cc..000000000000 --- a/semanticdb/input/src/main/scala/example/Imports.scala +++ /dev/null @@ -1 +0,0 @@ -//import scala.util.control.NonFatal diff --git a/semanticdb/input/src/main/scala/example/MacroAnnotations.scala b/semanticdb/input/src/main/scala/example/MacroAnnotations.scala index 02276ed2ef6f..50f8576ecf6f 100644 --- a/semanticdb/input/src/main/scala/example/MacroAnnotations.scala +++ b/semanticdb/input/src/main/scala/example/MacroAnnotations.scala @@ -1,5 +1,20 @@ +/* +// This text is deactivated for now as macro annotations require to enable +// macro paradise + package example -//@MacroAnnotation + +import scala.annotation.StaticAnnotation +import scala.annotation.compileTimeOnly +import scala.language.experimental.macros + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class MacroAnnotation extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = ??? +} + +@MacroAnnotation class MacroAnnotations object MacroAnnotations +*/ \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Types.scala b/semanticdb/input/src/main/scala/example/Types.scala index a22b49115ead..02a4ea156e49 100644 --- a/semanticdb/input/src/main/scala/example/Types.scala +++ b/semanticdb/input/src/main/scala/example/Types.scala @@ -3,9 +3,13 @@ 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 TypeM { + def m: Int = ??? +} + +class TypeC extends TypeM { + val superType = super[TypeM].m +} class TypB @@ -59,9 +63,6 @@ object TypTest { 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") diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 43f1b8e666ad..d2598d62d689 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -106,7 +106,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => false } - def isTypeParameter: Boolean = symbol.flags.is(Flags.Param) && symbol.isType + def isTypeParameter: Boolean = symbol.isParameter && symbol.isType def isType: Boolean = symbol match { case IsTypeSymbol(_) => true @@ -128,13 +128,21 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isDefaultGetter: Boolean = symbol.name.contains(tpnme.DEFAULT_GETTER.toString) + def isReservedName : Boolean = { + val keywords = + List("ev$", "evidence$", "$_lazy_implicit_$", "$lzy", "$lzyINIT", + "$OFFSET", "bitmap$", "_$", "$tailLocal", "tmp", "$doc", + "$superArg$", "$scrutinee", "$elem") + return keywords.exists(symbol.name.contains(_)) + } + def isParameter: Boolean = symbol.flags.is(Flags.Param) def isObject: Boolean = symbol.flags.is(Flags.Object) def isTrait: Boolean = symbol.flags.is(Flags.Trait) - def isValueParameter: Boolean = symbol.flags.is(Flags.Param) + def isValueParameter: Boolean = symbol.isParameter && !symbol.isType def isJavaClass: Boolean = symbol.isClass && symbol.flags.is(Flags.JavaDefined) @@ -263,6 +271,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || + symbol.isReservedName || //symbol.isInitChild || symbol.isAnonymousInit || symbol.isDefaultGetter || @@ -347,14 +356,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => d.Term(symbol.trueName) } + } else if (symbol.isValueParameter) { + d.Parameter(symbol.trueName) } else if (symbol.isMethod || symbol.isUsefulField) { d.Method(symbol.trueName, disimbiguate(previous_symbol + symbol.trueName, symbol)) } else if (symbol.isTypeParameter) { d.TypeParameter(symbol.trueName) - } else if (symbol.isValueParameter) { - d.Parameter(symbol.trueName) - } else if (symbol.isType || symbol.isTrait) { + } else if (symbol.isType || symbol.isTrait) { d.Type(symbol.trueName) } else { d.Term(symbol.trueName) @@ -413,6 +422,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => symbolToSymbolString(symbol) } + + println(symbol_path) // We want to add symbols coming from our file // if (symbol.pos.sourceFile != sourceFile) return if (symbol_path == "" || symbol.isUselessOccurrence) return @@ -426,9 +437,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { // dotty will generate a ValDef for the x, but the x will also // be present in the constructor, thus making a double definition if (symbolPathsMap.contains(key)) return - if (is_global) { + //if (is_global) { symbolPathsMap += key - } + //} println(symbol_path, range, symbol.flags, @@ -589,13 +600,50 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { s.SymbolOccurrence.Role.REFERENCE, range) super.traverseTypeTree(typetree) - }/* + } + + case TypeTree.Projection(qualifier, x) => { + val typetree = extractTypeTree(tree) + val range = rangeSelect(typetree.symbol.trueName, typetree.pos) + addOccurenceTypeTree(typetree, + s.SymbolOccurrence.Role.REFERENCE, + range) + super.traverseTypeTree(typetree) + } + case TypeTree.Inferred() => { + /* In theory no inferred types should be put in the semanticdb file. + However, take the case where a typed is refered from an imported class: + class PrefC { + object N { + type U + } + } + + object PrefTest { + val c: PrefC = ??? + import c.N._ + def k3: U = ??? + } + + The type corresponding to U in the definition of k3 is marked as + inferred even though it is present in the source code. We use a + workaround for this specific case, by checking if the name of the + inferred type corresponds to the one put in the source code at this + position + */ + val typetree = extractTypeTree(tree) - addOccurenceTypeTree(typetree, + val start = typetree.pos.start + val end = typetree.pos.end + if (end < sourceCode.length + && sourceCode.substring(start, end) == typetree.symbol.name) { + addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, posToRange(typetree.pos).get) - }*/ + } + } + case _ => { super.traverseTypeTree(tree) } @@ -622,6 +670,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { var fittedInitClassRange: Option[s.Range] = None var forceAddBecauseParents: Boolean = false + var classStacks : List[Symbol] = Nil def getNumberParametersInit(defdef: DefDef)(implicit ctx: Context): Int = { defdef match { @@ -646,7 +695,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(tree) } case ClassDef(classname, constr, parents, selfopt, statements) => { - // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -677,39 +725,46 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } }) forceAddBecauseParents = false - selfopt match { - case Some(vdef @ ValDef(name, type_, _)) if name != "_" => { - // To find the current position, we will heuristically - // reparse the source code. - // The process is done in three steps: - // 1) Find a position before the '{' of the self but after any - // non related '{'. Here, it will be the largest end pos of a parent - // 2) Find the first '{' - // 3) Iterate until the character we are seeing is a letter - val startPosSearch: Int = parents.foldLeft(tree.pos.end)( - (old: Int, ct: TermOrTypeTree) => - ct match { - case IsTerm(t) if t.pos.end < old => t.pos.end - case _ => old - }) - var posColumn = sourceCode.indexOf("{", if (startPosSearch == tree.pos.end) tree.pos.start else startPosSearch) - - while (posColumn < sourceCode.length && !sourceCode(posColumn).isLetter) posColumn += 1 - - addSelfDefinition(name, - s.Range(0, - posColumn, - 0, - posColumn + name.length)) + case Some(vdef @ ValDef(name, type_, _)) => { + // If name is "_" then it means it is in fact "this". We don't + // want to had a symbol for it in semanticdb + if (name != "_") { + // The tree does not include a position to the overloaded version of + // this. We find it heuristically by "parsing" the source code. + // The process is done in three steps: + // 1) Find a position before the '{' of the self but after any + // non related '{'. Here, it will be the largest end pos of a parent + // 2) Find the first '{' + // 3) Iterate until the character we are seeing is a letter + val startPosSearch: Int = parents.foldLeft(tree.pos.end)( + (old: Int, ct: TermOrTypeTree) => + ct match { + case IsTerm(t) if t.pos.end < old => t.pos.end + case _ => old + }) + var posColumn = sourceCode.indexOf("{", if (startPosSearch == tree.pos.end) tree.pos.start else startPosSearch) + + while (posColumn < sourceCode.length && !sourceCode(posColumn).isLetter) posColumn += 1 + + addSelfDefinition(name, + s.Range(0, + posColumn, + 0, + posColumn + name.length)) + } println(type_) traverseTypeTree(type_) } case _ => } + classStacks = tree.symbol :: classStacks + statements.takeRight(statements.length - getNumberParametersInit(constr)).foreach(traverseTree) + classStacks = classStacks.tail + } case IsDefinition(cdef) => { @@ -761,12 +816,22 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(cdef) } - case Term.This(what) => + case Term.This(Some(_)) => { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, posToRange(tree.pos).get) + } + + case Term.Super(_, Some(id)) => + { + addOccurence(classStacks.head, + s.SymbolOccurrence.Role.DEFINITION, + posToRange(id.pos).get) + super.traverseTree(tree) + } case Term.Select(qualifier, _) => { + println("SELECT => " + tree) val range = { val r = rangeSelect(tree.symbol.trueName, tree.pos) if (tree.symbol.trueName == "") diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 5612909ca047..1d931aa14bd6 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -92,11 +92,10 @@ class Tests { @Test def testExample2(): Unit = checkFile("example/Example2.scala") @Test def testExclude(): Unit = checkFile("example/Exclude.scala") @Test def testFlags(): Unit = checkFile("example/Flags.scala") - @Test def testImports(): Unit = checkFile("example/Imports.scala") @Test def testIssue1749(): Unit = checkFile("example/Issue1749.scala") @Test def testLocalFile(): Unit = checkFile("example/local-file.scala") @Test def testLocals(): Unit = checkFile("example/Locals.scala") - @Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") + //deactivated @Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") @Test def testMethods(): Unit = checkFile("example/Methods.scala") @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") @Test def testObjects(): Unit = checkFile("example/Objects.scala") @@ -106,6 +105,7 @@ class Tests { @Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") @Test def testTraits(): Unit = checkFile("example/Traits.scala") @Test def testTypes(): Unit = checkFile("example/Types.scala") + @Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols @Test def testVals(): Unit = checkFile("example/Vals.scala") @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") @Test def testNew(): Unit = checkFile("example/New.scala") @@ -114,13 +114,10 @@ class Tests { @Test def testApply(): Unit = checkFile("example/Apply.scala") @Test def testMethodUsages(): Unit = checkFile("example/MethodUsages.scala") @Test def testSuper(): Unit = checkFile("example/Super.scala") - @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala")*/ - //@Test def testTraits(): Unit = checkFile("example/Traits.scala") + @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala") @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") - //@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") - //@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") + @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") // Failure @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") - @Test def testCase(): Unit = checkFile("example/Case.scala") - + */ - } +} From 8a838493fc949928b1764847034276440da50dd3 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 16 Jan 2019 12:28:44 +0100 Subject: [PATCH 29/42] Handle correct positions of reversed binary operators (eg #::) --- .../src/main/scala/example/BinaryOp.scala | 3 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 28 +++++++++++++++---- semanticdb/test/dotty/semanticdb/Tests.scala | 4 +-- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/BinaryOp.scala b/semanticdb/input/src/main/scala/example/BinaryOp.scala index 27929e243aa9..054c87f2b62f 100644 --- a/semanticdb/input/src/main/scala/example/BinaryOp.scala +++ b/semanticdb/input/src/main/scala/example/BinaryOp.scala @@ -2,5 +2,6 @@ package example class BinaryOp { val y = 1 #:: 2 #:: Stream.empty[Int] - val x = 1 + 2 + 3 + val x = 1 :: 2 :: 3 :: Nil + val z = 1 + 2 } \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index d2598d62d689..04a5e62b4e34 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -423,7 +423,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbolToSymbolString(symbol) } - println(symbol_path) + println(symbol_path + " : " + range) // We want to add symbols coming from our file // if (symbol.pos.sourceFile != sourceFile) return if (symbol_path == "" || symbol.isUselessOccurrence) return @@ -533,10 +533,26 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val len = if (name == "") 0 else name.length - return s.Range(range.endLine, - range.endColumn - len, - range.endLine, - range.endColumn) + /* The position of a select is the position of the whole select expression, + from the start to the end. + To get the position of only the selected operand, we distinguish two cases: + - either we are selecting an operator ending with a ':' (for those the execution + order is reversed), so the selected expression is at the start. + Ex: A #:: B -> the position of the select is the range "#:: B", so we pick the range "#::" + - either the select is in normal order, in this case we select the end of it. + Ex: A + B -> the position of the select is the range "A +", so we pick the range "+" + */ + if (name.endsWith(":")) { + return s.Range(range.startLine, + range.startColumn, + range.startLine, + range.startColumn + len) + } else { + return s.Range(range.endLine, + range.endColumn - len, + range.endLine, + range.endColumn) + } } def getImportPath(path_term: Term): String = { @@ -831,7 +847,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case Term.Select(qualifier, _) => { - println("SELECT => " + tree) val range = { val r = rangeSelect(tree.symbol.trueName, tree.pos) if (tree.symbol.trueName == "") @@ -841,6 +856,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { r.endCharacter + 1) else r } + println("SELECT => " + tree + tree.pos.start + ":" + tree.pos.end) addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, forceAddBecauseParents) super.traverseTree(tree) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 1d931aa14bd6..34eb6fbc91eb 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -116,8 +116,8 @@ class Tests { @Test def testSuper(): Unit = checkFile("example/Super.scala") @Test def testTypeBug(): Unit = checkFile("example/TypeBug.scala") @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") - @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") // Failure + @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") */ - + @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") } From 30257def3fc55d656a3ef430fa0ba5d87f92e26b Mon Sep 17 00:00:00 2001 From: poechsel Date: Sat, 19 Jan 2019 00:52:14 +0100 Subject: [PATCH 30/42] Improvement on class constructor handling --- .../src/main/scala/example/Classes.scala | 28 ++- .../src/main/scala/example/Example.scala | 1 + .../input/src/main/scala/example/New.scala | 8 + .../src/main/scala/example/SemanticDoc.scala | 12 + .../input/src/main/scala/example/Types.scala | 6 +- .../main/scala/example/TypesAnnotations.scala | 13 + .../dotty/semanticdb/SemanticdbConsumer.scala | 225 ++++++++++++++---- semanticdb/test/dotty/semanticdb/Tests.scala | 10 +- 8 files changed, 248 insertions(+), 55 deletions(-) create mode 100644 semanticdb/input/src/main/scala/example/SemanticDoc.scala create mode 100644 semanticdb/input/src/main/scala/example/TypesAnnotations.scala diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala index b82f2c030ff0..d5c0d3f3970b 100644 --- a/semanticdb/input/src/main/scala/example/Classes.scala +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -1,14 +1,25 @@ package example +class CDep[X] +class CDependenat[X](var x1: CDep[X]) +class CVarArg(var x1 : Int) + + +class CDefaultWrapper { + val glob = 3 + class Cdefault(val x1: Int = glob) +} class C1(val x1: Int) extends AnyVal class C2(val x2: Int) extends AnyVal object C2 -case class C3(x: Int) +case class C3[Y ](x: Int) case class C4(x: Int) -object C4 +object C4 { + val foo: Int = 4 +} object M { implicit class C5(x: Int) @@ -30,4 +41,15 @@ object N { val local = 2 local + 2 } -} + + val otheranon = { + var a = 1 + a + } + + val lastanon = { + val a = new CVarArg(4) + a.x1 = 8 + a.x1 + } +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Example.scala b/semanticdb/input/src/main/scala/example/Example.scala index 4e431eccf47a..682c0ba7cea6 100644 --- a/semanticdb/input/src/main/scala/example/Example.scala +++ b/semanticdb/input/src/main/scala/example/Example.scala @@ -14,6 +14,7 @@ class Example { x + y ) + var b = 4 } class ExampleInit { diff --git a/semanticdb/input/src/main/scala/example/New.scala b/semanticdb/input/src/main/scala/example/New.scala index ee76dc8e2d00..77f32b0a7c33 100644 --- a/semanticdb/input/src/main/scala/example/New.scala +++ b/semanticdb/input/src/main/scala/example/New.scala @@ -3,7 +3,15 @@ package example class Bonjour() { } +class Bonjour2(val x:Int) { + def this(x:String) = this(2) +} + class TestNew extends C { val b = new B val c = new Bonjour + val d = new Bonjour() + val e = new Bonjour2(2) + val f = new Bonjour2("a") + val _ = f.x } \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/SemanticDoc.scala b/semanticdb/input/src/main/scala/example/SemanticDoc.scala new file mode 100644 index 000000000000..56e19d4118c7 --- /dev/null +++ b/semanticdb/input/src/main/scala/example/SemanticDoc.scala @@ -0,0 +1,12 @@ +package example + +abstract class Ctest(val xp: Int, val xe : AnyRef) { + val xm: Int = ??? + val xam: Int + private[this] val xlm: Int = ??? + def m = { + val xl: Int = ??? + type S = { val xs: Int } + type E = xe.type + } +} \ No newline at end of file diff --git a/semanticdb/input/src/main/scala/example/Types.scala b/semanticdb/input/src/main/scala/example/Types.scala index 02a4ea156e49..7054828b53bf 100644 --- a/semanticdb/input/src/main/scala/example/Types.scala +++ b/semanticdb/input/src/main/scala/example/Types.scala @@ -36,6 +36,10 @@ object TypTest { def n: Int = ??? } + class B { + def x:Int = 4 + } + class C extends M { val p = new TypP val x = p.x @@ -71,7 +75,7 @@ object TypTest { typeLambda1[({ type L[T] = List[T] })#L] object ClassInfoType1 - class ClassInfoType2 extends B { def x = 42 } + class ClassInfoType2 extends B { override def x = 42 } trait ClassInfoType3[T] object MethodType { diff --git a/semanticdb/input/src/main/scala/example/TypesAnnotations.scala b/semanticdb/input/src/main/scala/example/TypesAnnotations.scala new file mode 100644 index 000000000000..c4ef702686fc --- /dev/null +++ b/semanticdb/input/src/main/scala/example/TypesAnnotations.scala @@ -0,0 +1,13 @@ +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 + +object TypTestAnnots { + val annType1: T @ann(42) = ??? + val annType2: T @ann1 @ann2 = ??? +} diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 04a5e62b4e34..eafb1cb3f72b 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -23,7 +23,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { s.TextDocument(text = sourceCode, occurrences = occurrences) } val package_definitions: Set[Tuple2[String, Int]] = Set() - val symbolsCache: HashMap[(String, s.Range), String] = HashMap() + val symbolsCache: HashMap[(Any, s.Range), String] = HashMap() var local_offset: Int = 0 val sourceCode = Source.fromFile(sourceFile.toFile).mkString @@ -120,6 +120,11 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => false } + def isVal: Boolean = symbol match { + case IsValSymbol(_) => true + case _ => false + } + def isPackage: Boolean = symbol match { case IsPackageSymbol(_) => true case _ => false @@ -142,7 +147,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isTrait: Boolean = symbol.flags.is(Flags.Trait) - def isValueParameter: Boolean = symbol.isParameter && !symbol.isType + def isValueParameter: Boolean = symbol.isParameter && !symbol.isType && !symbol.flags.is(Flags.ParamAccessor) def isJavaClass: Boolean = symbol.isClass && symbol.flags.is(Flags.JavaDefined) @@ -191,18 +196,24 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } else { symbol.flags.is(Flags.ModuleClass) && symbol.flags.is(Flags.Synthetic) && - symbol.asClass.methods.length == 0 + symbol.asClass.methods.isEmpty } } else { false } } + + def isVarAccessor(implicit ctx: Context): Boolean = { + symbol.isVal && symbol.flags.is(Flags.Mutable) + } + def isValMethod(implicit ctx: Context): Boolean = { symbol.isMethod && { (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.Stable) ) || (symbol.isUsefulField && !symbol.flags.is(Flags.Mutable) ) } } + def isScalacField(implicit ctx: Context): Boolean = { val isFieldForPrivateThis = symbol.flags.is(Flags.PrivateLocal) && symbol.isTerm && !symbol.isMethod && !symbol.isObject val isFieldForOther = false //symbol.name.endsWith(g.nme.LOCAL_SUFFIX_STRING) @@ -210,7 +221,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { (isFieldForPrivateThis || isFieldForOther) && !isJavaDefined } def isUselessField(implicit ctx: Context): Boolean = { - symbol.isScalacField && false /*symbol.getterIn(symbol.owner) != g.NoSymbol*/ + symbol.isScalacField && symbol.owner != NoSymbol } def isUsefulField(implicit ctx: Context): Boolean = { symbol.isScalacField && !symbol.isUselessField @@ -272,7 +283,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isUseless(implicit ctx: Context): Boolean = { symbol == NoSymbol || symbol.isReservedName || - //symbol.isInitChild || symbol.isAnonymousInit || symbol.isDefaultGetter || symbol.isWildCard || @@ -322,8 +332,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { "(+" + real_pos + ")" } } - case _ => + case _ => { "()" + } } } catch { case _ => "()" @@ -341,11 +352,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. This is illogic: a init method can't have any child. Thus, when the current symbol is a typeparameter (or anything), and the owner is an init, we can just "jump" over the init. */ - if (symbol.owner.name == "") + if (symbol.owner.name == "" && symbol.isType) iterateParent(symbol.owner.owner) else iterateParent(symbol.owner) + + val isdef = symbol match {case IsDefSymbol(_) => true case _ => false} val next_atom = if (symbol.isPackage) { d.Package(symbol.trueName) @@ -356,14 +369,17 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => d.Term(symbol.trueName) } - } else if (symbol.isValueParameter) { - d.Parameter(symbol.trueName) - } else if (symbol.isMethod || symbol.isUsefulField) { + } else if (symbol.isValMethod && !symbol.isVarAccessor) { + d.Term(symbol.trueName) + } else if (symbol.isMethod || symbol.isUsefulField || symbol.isVarAccessor) { + println("METHOD", symbol.isMethod, symbol.isUsefulField) d.Method(symbol.trueName, disimbiguate(previous_symbol + symbol.trueName, symbol)) } else if (symbol.isTypeParameter) { d.TypeParameter(symbol.trueName) - } else if (symbol.isType || symbol.isTrait) { + } else if (symbol.isValueParameter) { + d.Parameter(symbol.trueName) + } else if (symbol.isType || symbol.isTrait) { d.Type(symbol.trueName) } else { d.Term(symbol.trueName) @@ -410,10 +426,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { range: s.Range): Unit = { if (symbol.name == "") return + + println("===> ", symbol, symbol.flags) + val (symbol_path, is_global) = posToRange(symbol.pos) match { case Some(keyRange) - if symbolsCache.contains((symbol.trueName, keyRange)) => - (symbolsCache((symbol.trueName, keyRange)), symbol.isSemanticdbLocal) + if symbolsCache.contains((symbol.trueName, keyRange)) => { + println("already found") + (symbolsCache((symbol.trueName, keyRange)), symbol.isSemanticdbLocal)} case Some(keyRange) => { val (sp, ig) = symbolToSymbolString(symbol) symbolsCache += ((symbol.trueName, keyRange) -> sp) @@ -423,10 +443,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbolToSymbolString(symbol) } - println(symbol_path + " : " + range) + println(symbol_path, symbol.isUselessOccurrence) // We want to add symbols coming from our file // if (symbol.pos.sourceFile != sourceFile) return - if (symbol_path == "" || symbol.isUselessOccurrence) return + if (symbol_path == "" /*|| symbol.isUselessOccurrence*/) return if (symbol.flags.is(Flags.Synthetic) && type_symbol == s.SymbolOccurrence.Role.DEFINITION) return val key = (symbol_path, range) @@ -442,9 +462,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { //} println(symbol_path, range, - symbol.flags, - is_global, - iterateParent(symbol)) + symbol.flags) occurrences = occurrences :+ s.SymbolOccurrence( @@ -459,19 +477,17 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false): Unit = { - if (type_symbol != s.SymbolOccurrence.Role.DEFINITION && reservedFunctions - .contains(tree.symbol.trueName)) - return - if (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( - tree.symbol) == "java/lang/Object#``()."))) { + println("coming from tree") + if (tree.symbol.isUseful && (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( + tree.symbol) == "java/lang/Object#``().")))) { addOccurence(tree.symbol, type_symbol, range) } } def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - println(typetree.isUserCreated) - if (typetree.isUserCreated) { + println("coming from type") + if (typetree.symbol.isUseful && typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } } @@ -530,9 +546,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } def rangeSelect(name: String, range: Position): s.Range = { - val len = - if (name == "") 0 - else name.length + if (name == "") { + println(posToRange(range).get) + return posToRange(range).get + } else /* The position of a select is the position of the whole select expression, from the start to the end. To get the position of only the selected operand, we distinguish two cases: @@ -546,10 +563,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { return s.Range(range.startLine, range.startColumn, range.startLine, - range.startColumn + len) + range.startColumn + name.length) } else { return s.Range(range.endLine, - range.endColumn - len, + range.endColumn - name.length, range.endLine, range.endColumn) } @@ -611,7 +628,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case TypeTree.Select(qualifier, _) => { val typetree = extractTypeTree(tree) val range = rangeSelect(typetree.symbol.trueName, typetree.pos) - println(typetree.pos.start, typetree.pos.end) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, range) @@ -696,6 +712,44 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + def nextCharacterSkipComments(s : String, start : Int): Int = { + def aux(start : Int) : Int = { + var i = start + if (i+2 <= s.length && s.substring(i, i+2) == "//") { + while (i < s.length && s(i) != '\n') + i += 1 + return i+1 + } else if (i+2 <= s.length && s.substring(i, i+2) == "/*") { + while (i + 2 <= s.length && s.substring(i, i+2) != "*/") + i += 1 + return i+2 + } else { + while (i < s.length && s(i).isWhitespace) + i += 1 + return i + } + } + var previous = start + var next = aux(previous) + while (previous != next) { + previous = next + next = aux(previous) + } + return previous + } + + var disableConstrParamTraversal = false + + def generateParamsPosMapping(cdef: DefDef)(implicit ctx: Context): Map[String, s.Range] = { + val DefDef(_, _, params, _, _) = cdef + val start = Map[String, s.Range]() + return params.foldLeft(start)((old, statements) => + return statements.foldLeft(old)((old, cval) => + return old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName)) + ) + ) + } + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Import(path, selectors) => @@ -710,12 +764,31 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Apply(_, _) => { super.traverseTree(tree) } - case ClassDef(classname, constr, parents, selfopt, statements) => { + case cl @ ClassDef(classname, constr, parents, selfopt, statements) => { // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, range(tree, tree.symbol.pos, tree.symbol.trueName)) // then the constructor + + val DefDef(_, typesParameters, _, _, _) = constr + if (typesParameters.isEmpty) { + fittedInitClassRange = Some( + s.Range(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length, + tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + classname.length)) + } else { + val rightmost = typesParameters.reverse.head.pos.end + val end_ = nextCharacterSkipComments(sourceCode, rightmost) + 1 + fittedInitClassRange = Some( + s.Range(tree.symbol.pos.startLine, + end_, + tree.symbol.pos.startLine, + end_)) + } + +/*s if (!constr.isUserCreated) { fittedInitClassRange = Some( s.Range(tree.symbol.pos.startLine, @@ -728,8 +801,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { constr.symbol.pos.startColumn, constr.symbol.pos.endLine, constr.symbol.pos.endColumn)) - } - traverseTree(constr) + }*/ + println(tree.symbol.flags, cl.symbol.asClass.companionClass, tree.symbol, constr.pos.start, constr.pos.end) + + disableConstrParamTraversal = true + traverseTree(constr) + disableConstrParamTraversal = false + fittedInitClassRange = None // we add the parents to the symbol list @@ -769,19 +847,58 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { 0, posColumn + name.length)) } - println(type_) traverseTypeTree(type_) } case _ => } - + if (!tree.symbol.flags.is(Flags.Case)) { classStacks = tree.symbol :: classStacks - statements.takeRight(statements.length - getNumberParametersInit(constr)).foreach(traverseTree) + println("\n>>>>>>>>>" + classStacks) + println(statements) + + val paramsPosMapping = generateParamsPosMapping(constr) + + println("####" + paramsPosMapping) + + statements.foreach(statement => { + if (statement.symbol.flags.is(Flags.ParamAccessor)) { + println("oki", statement.symbol.pos) + if (paramsPosMapping.contains(statement.symbol.name)) { + addOccurence(statement.symbol, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name)) + } + //traverseTree(statement) + } else if (!statement.symbol.flags.is(Flags.Param)) { + traverseTree(statement) + } + }) + println("<<<<<<<<<<" + classStacks + "\n" ) classStacks = classStacks.tail + } } + + case DefDef("", typeparams, valparams, type_, statements) if fittedInitClassRange != None => { + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + fittedInitClassRange.get, + true) + valparams.foreach(params => + params.foreach(param => { + val ValDef(_, tpt, _) = param + traverseTypeTree(tpt) + }) + ) + traverseTypeTree(type_) + typeparams.foreach(traverseTree) + } + + /*case Term.Assign(lhs, rhs) => { + println("ASSIGN:", lhs.symbol, lhs.symbol.flags, rhs.symbol, rhs.symbol.flags) + super.traverseTree(tree) + }*/ + case IsDefinition(cdef) => { if (cdef.symbol.flags.is(Flags.Protected)) { @@ -816,7 +933,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } if (tree.symbol.trueName != "") { - val range_symbol = range(tree, tree.symbol.pos, tree.symbol.trueName) + val pos = tree.symbol.pos + var rangeSymbol = range(tree, pos, tree.symbol.trueName) + + if (tree.symbol.trueName == "" && pos.start + 4 < sourceCode.length + && sourceCode.substring(pos.start, pos.start + 4) == "this") { + rangeSymbol = s.Range(pos.startLine, pos.startColumn, pos.startLine, pos.startColumn + 4) + } + if (tree.symbol.trueName == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { } else if (tree.symbol.trueName == "" && fittedInitClassRange != None) { addOccurenceTree(tree, @@ -826,9 +950,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } else { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, - range_symbol) + rangeSymbol) } } + //if (!disableConstrParamTraversal) super.traverseTree(cdef) } @@ -847,18 +972,20 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case Term.Select(qualifier, _) => { - val range = { - val r = rangeSelect(tree.symbol.trueName, tree.pos) - if (tree.symbol.trueName == "") - s.Range(r.startLine, - r.startCharacter + 1, - r.endLine, - r.endCharacter + 1) - else r + var range = rangeSelect(tree.symbol.trueName, tree.pos) + + var shouldForceAdd = false + if (tree.symbol.trueName == "") { + if (tree.pos.start == tree.pos.end && 5 <= tree.pos.start && sourceCode.substring(tree.pos.start - 5, tree.pos.start - 1) == "this") { + range = s.Range(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, tree.pos.start - 1) + shouldForceAdd = true + } else { + shouldForceAdd = qualifier.isUserCreated + } } println("SELECT => " + tree + tree.pos.start + ":" + tree.pos.end) - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, forceAddBecauseParents) super.traverseTree(tree) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd) } case Term.Ident(name) => { @@ -900,7 +1027,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val key = (tree.symbol.trueName, tree.pos.start) if (!package_definitions(key)) { addOccurenceTree(tree, - s.SymbolOccurrence.Role.REFERENCE, + s.SymbolOccurrence.Role.DEFINITION, range(tree, tree.pos, tree.symbol.trueName)) package_definitions += key } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 34eb6fbc91eb..1ca35edc1aa0 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -51,6 +51,7 @@ class Tests { println(tasty) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) + print("X=>",scalac.occurrences) assertNoDiff(obtained, expected) } @@ -119,5 +120,10 @@ class Tests { @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") */ - @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") -} + @Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols + @Test def testNew(): Unit = checkFile("example/New.scala") + @Test def testClasses(): Unit = checkFile("example/Classes.scala") + + @Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala") + @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") +} \ No newline at end of file From 1b584680e223f577cfc88ee4e2eb40dada643d0e Mon Sep 17 00:00:00 2001 From: poechsel Date: Sat, 19 Jan 2019 01:20:01 +0100 Subject: [PATCH 31/42] Infer var setter usage when assigning value to a var --- .../dotty/semanticdb/SemanticdbConsumer.scala | 62 +++++++++++-------- semanticdb/test/dotty/semanticdb/Tests.scala | 8 +-- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index eafb1cb3f72b..fb15c3072158 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -341,7 +341,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } - def iterateParent(symbol: Symbol): String = { + def iterateParent(symbol: Symbol, isMutableAssignement:Boolean=false): String = { if (symbol.name == "" || symbol.name == "") then { // TODO had a "NoDenotation" test to avoid // relying on the name itself @@ -359,30 +359,32 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val isdef = symbol match {case IsDefSymbol(_) => true case _ => false} + val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName + println("=>", symbolName) val next_atom = if (symbol.isPackage) { - d.Package(symbol.trueName) + d.Package(symbolName) } else if (symbol.isObject) { symbol match { case IsClassSymbol(classsymbol) => d.Term(resolveClass(classsymbol).trueName) case _ => - d.Term(symbol.trueName) + d.Term(symbolName) } } else if (symbol.isValMethod && !symbol.isVarAccessor) { - d.Term(symbol.trueName) + d.Term(symbolName) } else if (symbol.isMethod || symbol.isUsefulField || symbol.isVarAccessor) { println("METHOD", symbol.isMethod, symbol.isUsefulField) - d.Method(symbol.trueName, - disimbiguate(previous_symbol + symbol.trueName, symbol)) + d.Method(symbolName, + disimbiguate(previous_symbol + symbolName, symbol)) } else if (symbol.isTypeParameter) { - d.TypeParameter(symbol.trueName) + d.TypeParameter(symbolName) } else if (symbol.isValueParameter) { - d.Parameter(symbol.trueName) + d.Parameter(symbolName) } else if (symbol.isType || symbol.isTrait) { - d.Type(symbol.trueName) + d.Type(symbolName) } else { - d.Term(symbol.trueName) + d.Term(symbolName) } Symbols.Global(previous_symbol, next_atom) @@ -411,32 +413,33 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } - def symbolToSymbolString(symbol: Symbol): (String, Boolean) = { + def symbolToSymbolString(symbol: Symbol, isMutableAssignement:Boolean = false): (String, Boolean) = { if (symbol.isSemanticdbLocal) { var localsymbol = Symbols.Local(local_offset.toString) local_offset += 1 (localsymbol, false) } else { - (iterateParent(symbol), true) + (iterateParent(symbol, isMutableAssignement), true) } } def addOccurence(symbol: Symbol, type_symbol: s.SymbolOccurrence.Role, - range: s.Range): Unit = { + range: s.Range, + isMutableAssignement:Boolean = false): Unit = { if (symbol.name == "") return println("===> ", symbol, symbol.flags) - + val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName val (symbol_path, is_global) = posToRange(symbol.pos) match { case Some(keyRange) - if symbolsCache.contains((symbol.trueName, keyRange)) => { + if symbolsCache.contains((symbolName, keyRange)) => { println("already found") - (symbolsCache((symbol.trueName, keyRange)), symbol.isSemanticdbLocal)} + (symbolsCache((symbolName, keyRange)), symbol.isSemanticdbLocal)} case Some(keyRange) => { - val (sp, ig) = symbolToSymbolString(symbol) - symbolsCache += ((symbol.trueName, keyRange) -> sp) + val (sp, ig) = symbolToSymbolString(symbol, isMutableAssignement) + symbolsCache += ((symbolName, keyRange) -> sp) (sp, ig) } case _ => @@ -476,11 +479,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, - force_add: Boolean = false): Unit = { - println("coming from tree") + force_add: Boolean = false, + isMutableAssignement: Boolean = false): Unit = { + println("coming from tree", isMutableAssignement) if (tree.symbol.isUseful && (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( tree.symbol) == "java/lang/Object#``().")))) { - addOccurence(tree.symbol, type_symbol, range) + addOccurence(tree.symbol, type_symbol, range, isMutableAssignement) } } def addOccurenceTypeTree(typetree: TypeTree, @@ -750,6 +754,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } + var isAssignedTerm = false + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Import(path, selectors) => @@ -894,10 +900,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { typeparams.foreach(traverseTree) } - /*case Term.Assign(lhs, rhs) => { + case Term.Assign(lhs, rhs) => { println("ASSIGN:", lhs.symbol, lhs.symbol.flags, rhs.symbol, rhs.symbol.flags) + isAssignedTerm = true super.traverseTree(tree) - }*/ + isAssignedTerm = false + } case IsDefinition(cdef) => { @@ -983,9 +991,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { shouldForceAdd = qualifier.isUserCreated } } - println("SELECT => " + tree + tree.pos.start + ":" + tree.pos.end) + println("SELECT => " + tree + tree.symbol.flags) + println("acc", isAssignedTerm && tree.symbol.flags.is(Flags.Mutable)) + val temp = isAssignedTerm + isAssignedTerm = false super.traverseTree(tree) - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd) + isAssignedTerm = temp + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable)) } case Term.Ident(name) => { diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 1ca35edc1aa0..b1244d86d4c4 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -120,10 +120,10 @@ class Tests { @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") */ - @Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols - @Test def testNew(): Unit = checkFile("example/New.scala") + //@Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols + //@Test def testNew(): Unit = checkFile("example/New.scala") @Test def testClasses(): Unit = checkFile("example/Classes.scala") - @Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala") - @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") + //@Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala") + //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") } \ No newline at end of file From 25a0fd1c822a7278e505b40570ad2a815149f5c9 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sat, 19 Jan 2019 02:21:11 +0100 Subject: [PATCH 32/42] fix bug with var definition and protected[this] var and vals --- .../input/src/main/scala/example/Access.scala | 16 +++++ .../dotty/semanticdb/SemanticdbConsumer.scala | 71 +++++++++++-------- semanticdb/test/dotty/semanticdb/Tests.scala | 3 +- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Access.scala b/semanticdb/input/src/main/scala/example/Access.scala index aee3754da66b..eba966f117e4 100644 --- a/semanticdb/input/src/main/scala/example/Access.scala +++ b/semanticdb/input/src/main/scala/example/Access.scala @@ -8,4 +8,20 @@ class Access { protected[this] def m5 = ??? protected[example] def m6 = ??? def m7 = ??? + + private val mv1 = ??? + private[this] val mv2 = ??? + private[Access] val mv3 = ??? + protected val mv4 = ??? + protected[this] val mv5 = ??? + protected[example] val mv6 = ??? + val mv7 = ??? + + private var mr1 = ??? + private[this] var mr2 = ??? + private[Access] var mr3 = ??? + protected var mr4 = ??? + protected[this] var mr5 = ??? + protected[example] var mr6 = ??? + var mr7 = ??? } diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index fb15c3072158..9ac215838e47 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -66,10 +66,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def isUserCreated: Boolean = { val children: List[Position] = ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) - /*println("call to isusercreated on " + iterateParent(tree.symbol)) - if (tree.pos.exists) - println(tree.pos.start, tree.pos.end) - println(children.map(x => (x.start, x.end)))*/ return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children .exists(_ == tree.pos)) } @@ -214,11 +210,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + /* the `isFieldForPrivateThis` is commented out otherwise class members of the form + "private[this] val foo" are not converted to symbol occurences. + In the original semanticdb this line is commented out. + */ def isScalacField(implicit ctx: Context): Boolean = { - val isFieldForPrivateThis = symbol.flags.is(Flags.PrivateLocal) && symbol.isTerm && !symbol.isMethod && !symbol.isObject + //val isFieldForPrivateThis = symbol.flags.is(Flags.PrivateLocal) && symbol.isTerm && !symbol.isMethod && !symbol.isObject val isFieldForOther = false //symbol.name.endsWith(g.nme.LOCAL_SUFFIX_STRING) val isJavaDefined = symbol.flags.is(Flags.JavaDefined) - (isFieldForPrivateThis || isFieldForOther) && !isJavaDefined + (/*isFieldForPrivateThis ||*/ isFieldForOther) && !isJavaDefined } def isUselessField(implicit ctx: Context): Boolean = { symbol.isScalacField && symbol.owner != NoSymbol @@ -360,7 +360,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val isdef = symbol match {case IsDefSymbol(_) => true case _ => false} val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName - println("=>", symbolName) val next_atom = if (symbol.isPackage) { d.Package(symbolName) @@ -374,7 +373,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } else if (symbol.isValMethod && !symbol.isVarAccessor) { d.Term(symbolName) } else if (symbol.isMethod || symbol.isUsefulField || symbol.isVarAccessor) { - println("METHOD", symbol.isMethod, symbol.isUsefulField) d.Method(symbolName, disimbiguate(previous_symbol + symbolName, symbol)) } else if (symbol.isTypeParameter) { @@ -435,7 +433,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val (symbol_path, is_global) = posToRange(symbol.pos) match { case Some(keyRange) if symbolsCache.contains((symbolName, keyRange)) => { - println("already found") (symbolsCache((symbolName, keyRange)), symbol.isSemanticdbLocal)} case Some(keyRange) => { val (sp, ig) = symbolToSymbolString(symbol, isMutableAssignement) @@ -446,7 +443,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbolToSymbolString(symbol) } - println(symbol_path, symbol.isUselessOccurrence) // We want to add symbols coming from our file // if (symbol.pos.sourceFile != sourceFile) return if (symbol_path == "" /*|| symbol.isUselessOccurrence*/) return @@ -475,22 +471,39 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { ) } + + /* Return true if symbol represents the definition of a var setter, false otherwise. + We return true if the extract of source code corresponding to the position of the symbol is the same as the symbol name. + Ex: + var m = ??? + -> there is a defdef for `m_=` with position "m =". As "m =" != "m_=", we return false + */ + def isMutableSetterExplicit(symbol : Symbol, role : s.SymbolOccurrence.Role) = { + if (role == s.SymbolOccurrence.Role.DEFINITION && + symbol.pos.exists && + symbol.flags.is(Flags.Mutable) && symbol.isMethod && + symbol.trueName.endsWith("_=")) + (sourceCode.substring(symbol.pos.start, symbol.pos.end) == symbol.trueName) + else + true + } + val reservedFunctions: List[String] = Nil def addOccurenceTree(tree: Tree, type_symbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false, isMutableAssignement: Boolean = false): Unit = { - println("coming from tree", isMutableAssignement) - if (tree.symbol.isUseful && (tree.isUserCreated || (force_add && !(!tree.isUserCreated && iterateParent( - tree.symbol) == "java/lang/Object#``().")))) { + if (tree.symbol.isUseful && + isMutableSetterExplicit(tree.symbol, type_symbol) && + (tree.isUserCreated || + (force_add && !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``().")))) { addOccurence(tree.symbol, type_symbol, range, isMutableAssignement) } } def addOccurenceTypeTree(typetree: TypeTree, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - println("coming from type") if (typetree.symbol.isUseful && typetree.isUserCreated) { addOccurence(typetree.symbol, type_symbol, range) } @@ -551,7 +564,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def rangeSelect(name: String, range: Position): s.Range = { if (name == "") { - println(posToRange(range).get) return posToRange(range).get } else /* The position of a select is the position of the whole select expression, @@ -747,10 +759,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def generateParamsPosMapping(cdef: DefDef)(implicit ctx: Context): Map[String, s.Range] = { val DefDef(_, _, params, _, _) = cdef val start = Map[String, s.Range]() - return params.foldLeft(start)((old, statements) => - return statements.foldLeft(old)((old, cval) => - return old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName)) - ) + params.foldLeft(start)((old, statements) => { + statements.foldLeft(old)((old, cval) => { + println(cval) + old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName)) + }) + } ) } @@ -808,7 +822,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { constr.symbol.pos.endLine, constr.symbol.pos.endColumn)) }*/ - println(tree.symbol.flags, cl.symbol.asClass.companionClass, tree.symbol, constr.pos.start, constr.pos.end) disableConstrParamTraversal = true traverseTree(constr) @@ -860,25 +873,24 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (!tree.symbol.flags.is(Flags.Case)) { classStacks = tree.symbol :: classStacks - println("\n>>>>>>>>>" + classStacks) println(statements) - + println("") val paramsPosMapping = generateParamsPosMapping(constr) - - println("####" + paramsPosMapping) + println(paramsPosMapping) + println("") statements.foreach(statement => { if (statement.symbol.flags.is(Flags.ParamAccessor)) { - println("oki", statement.symbol.pos) if (paramsPosMapping.contains(statement.symbol.name)) { + println("parameter "+statement) addOccurence(statement.symbol, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name)) } //traverseTree(statement) } else if (!statement.symbol.flags.is(Flags.Param)) { + println(statement.symbol, statement.symbol.flags) traverseTree(statement) } }) - println("<<<<<<<<<<" + classStacks + "\n" ) classStacks = classStacks.tail } @@ -901,14 +913,14 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case Term.Assign(lhs, rhs) => { - println("ASSIGN:", lhs.symbol, lhs.symbol.flags, rhs.symbol, rhs.symbol.flags) isAssignedTerm = true super.traverseTree(tree) isAssignedTerm = false } case IsDefinition(cdef) => { - + println("definition " + cdef.symbol + " " + cdef.symbol.flags) + println(cdef.symbol.protectedWithin, cdef.symbol.privateWithin) if (cdef.symbol.flags.is(Flags.Protected)) { cdef.symbol.protectedWithin match { case Some(within) => { @@ -927,6 +939,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } else { cdef.symbol.privateWithin match { case Some(within) => { + println("YES") val startColumn = cdef.pos.startColumn + "private[".length addOccurence( within.typeSymbol, @@ -991,8 +1004,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { shouldForceAdd = qualifier.isUserCreated } } - println("SELECT => " + tree + tree.symbol.flags) - println("acc", isAssignedTerm && tree.symbol.flags.is(Flags.Mutable)) val temp = isAssignedTerm isAssignedTerm = false super.traverseTree(tree) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index b1244d86d4c4..f1820ed56140 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -122,8 +122,9 @@ class Tests { */ //@Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols //@Test def testNew(): Unit = checkFile("example/New.scala") - @Test def testClasses(): Unit = checkFile("example/Classes.scala") + //@Test def testClasses(): Unit = checkFile("example/Classes.scala") //@Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala") + @Test def testAccess(): Unit = checkFile("example/Access.scala") //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") } \ No newline at end of file From 907153132d47f3fdd0a76356308a78de2f223119 Mon Sep 17 00:00:00 2001 From: poechsel Date: Sun, 20 Jan 2019 22:10:13 +0100 Subject: [PATCH 33/42] improve testing infrastructure and fix small bugs --- .../input/src/main/scala/example/Types.scala | 2 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 73 ++++++++-------- semanticdb/test/dotty/semanticdb/Tests.scala | 83 +++++++++++++++---- 3 files changed, 103 insertions(+), 55 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Types.scala b/semanticdb/input/src/main/scala/example/Types.scala index 7054828b53bf..904da9dbce76 100644 --- a/semanticdb/input/src/main/scala/example/Types.scala +++ b/semanticdb/input/src/main/scala/example/Types.scala @@ -117,4 +117,4 @@ object TypTest { final val javaEnum = java.nio.file.LinkOption.NOFOLLOW_LINKS final val clazzOf = classOf[Option[Int]] } -} +} \ No newline at end of file diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 9ac215838e47..9015f85f2a0f 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -281,7 +281,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } def isUseless(implicit ctx: Context): Boolean = { - symbol == NoSymbol || + (symbol.name == "" || symbol == NoSymbol) || symbol.isReservedName || symbol.isAnonymousInit || symbol.isDefaultGetter || @@ -296,6 +296,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { symbol.isSyntheticCaseAccessor || symbol.isRefinementClass || symbol.isSyntheticJavaModule + // isSyntheticJavaModule disable the symbol Class in + // Class.forName(???) to be recorded as Class is considered to + // be a class in dotty, not a typed. } def isUseful(implicit ctx: Context): Boolean = !symbol.isUseless def isUselessOccurrence(implicit ctx: Context): Boolean = { @@ -427,8 +430,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { isMutableAssignement:Boolean = false): Unit = { if (symbol.name == "") return - - println("===> ", symbol, symbol.flags) val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName val (symbol_path, is_global) = posToRange(symbol.pos) match { case Some(keyRange) @@ -456,12 +457,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { // dotty will generate a ValDef for the x, but the x will also // be present in the constructor, thus making a double definition if (symbolPathsMap.contains(key)) return - //if (is_global) { - symbolPathsMap += key - //} - println(symbol_path, - range, - symbol.flags) + + symbolPathsMap += key occurrences = occurrences :+ s.SymbolOccurrence( @@ -490,15 +487,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val reservedFunctions: List[String] = Nil def addOccurenceTree(tree: Tree, - type_symbol: s.SymbolOccurrence.Role, + typeSymbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false, isMutableAssignement: Boolean = false): Unit = { if (tree.symbol.isUseful && - isMutableSetterExplicit(tree.symbol, type_symbol) && + isMutableSetterExplicit(tree.symbol, typeSymbol) && (tree.isUserCreated || - (force_add && !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``().")))) { - addOccurence(tree.symbol, type_symbol, range, isMutableAssignement) + (force_add /*&& !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``().")*/))) { + addOccurence(tree.symbol, typeSymbol, range, isMutableAssignement) } } def addOccurenceTypeTree(typetree: TypeTree, @@ -761,7 +758,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val start = Map[String, s.Range]() params.foldLeft(start)((old, statements) => { statements.foldLeft(old)((old, cval) => { - println(cval) old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName)) }) } @@ -833,9 +829,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { forceAddBecauseParents = !(tree.symbol.flags.is(Flags.Case)) parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) - case IsTerm(t) => { - traverseTree(t) - } + case IsTerm(t) => traverseTree(t) }) forceAddBecauseParents = false selfopt match { @@ -870,31 +864,20 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } case _ => } - if (!tree.symbol.flags.is(Flags.Case)) { + classStacks = tree.symbol :: classStacks - println(statements) - println("") val paramsPosMapping = generateParamsPosMapping(constr) - println(paramsPosMapping) - println("") statements.foreach(statement => { if (statement.symbol.flags.is(Flags.ParamAccessor)) { if (paramsPosMapping.contains(statement.symbol.name)) { - println("parameter "+statement) - addOccurence(statement.symbol, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name)) + addOccurenceTree(statement, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name)) } - //traverseTree(statement) } else if (!statement.symbol.flags.is(Flags.Param)) { - println(statement.symbol, statement.symbol.flags) traverseTree(statement) } }) - - classStacks = classStacks.tail - } - } case DefDef("", typeparams, valparams, type_, statements) if fittedInitClassRange != None => { @@ -914,13 +897,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Assign(lhs, rhs) => { isAssignedTerm = true - super.traverseTree(tree) + traverseTree(lhs) isAssignedTerm = false + traverseTree(rhs) } case IsDefinition(cdef) => { - println("definition " + cdef.symbol + " " + cdef.symbol.flags) - println(cdef.symbol.protectedWithin, cdef.symbol.privateWithin) if (cdef.symbol.flags.is(Flags.Protected)) { cdef.symbol.protectedWithin match { case Some(within) => { @@ -939,7 +921,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } else { cdef.symbol.privateWithin match { case Some(within) => { - println("YES") val startColumn = cdef.pos.startColumn + "private[".length addOccurence( within.typeSymbol, @@ -978,10 +959,24 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(cdef) } - case Term.This(Some(_)) => { + case Term.This(Some(id)) => { + /* We've got two options here: + - either the this is explicit: eg C.this.XXX. In this case, the position is [C.this], but + we want to put the symbol on the C, so around id + - either it is not explicit (eg a.foo). We want to put the symbol only around the a. + Distinguishing between the two is easy. If the sourcecode between [pos.start; pos.end] ends + with a 'this', then we're in the first case, otherwise the second + */ + var rangeThis = posToRange(tree.pos).get + if (sourceCode.substring(tree.pos.start, tree.pos.end).endsWith("this")) { + rangeThis = range(tree, tree.pos, tree.symbol.trueName) + } + /*range = s.Range(tree.pos.startLine, tree.pos.startColumn, + tree.pos.endLine, + )*/ addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, - posToRange(tree.pos).get) + rangeThis) } case Term.Super(_, Some(id)) => @@ -1001,6 +996,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { range = s.Range(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, tree.pos.start - 1) shouldForceAdd = true } else { + range = s.Range(tree.pos.endLine, tree.pos.endColumn, tree.pos.endLine, tree.pos.endColumn) shouldForceAdd = qualifier.isUserCreated } } @@ -1008,7 +1004,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { isAssignedTerm = false super.traverseTree(tree) isAssignedTerm = temp - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable)) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable) && !tree.symbol.flags.is(Flags.PrivateLocal)) } case Term.Ident(name) => { @@ -1062,9 +1058,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } - println("{--------------------------------------}") - println(root) - println("{--------------------------------------}") Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index f1820ed56140..aaaf7dbca74d 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -14,8 +14,74 @@ import scala.tasty.Reflection import scala.tasty.file.TastyConsumer import java.lang.reflect.InvocationTargetException +import dotty.semanticdb.Scala._ + + class Tests { + def min(a: Int, b: Int) : Int = if (a > b) b else a + def max(a: Int, b: Int) : Int = if (a > b) a else b + def abs(a: Int, b: Int) : Int = max(a, b) - min(a, b) + def distance(r1 : s.Range, offsets : Array[Int])(r2 : s.Range) : Int = { + val s1 = offsets(max(r1.startLine, 0)) + r1.startCharacter + val s2 = offsets(max(r2.startLine, 0)) + r2.startCharacter + val e1 = offsets(max(r1.endLine, 0)) + r1.endCharacter + val e2 = offsets(max(r2.endLine, 0)) + r2.endCharacter + max(abs(s1, s2), abs(e1, e2)) + } + + def compareOccurences(tastyOccurences : Seq[s.SymbolOccurrence], + scalaOccurences : Seq[s.SymbolOccurrence], + sourceCode : String) + : Boolean= { + val lineToByte = sourceCode.split("\n").scanLeft(0)((o, l) => o + l.length + 1) + val symbols = tastyOccurences.groupBy(_.symbol) + val localTastyToScala = HashMap[String, String]() + val localScalaToTasty = HashMap[String, String]() + val translator = HashMap[(s.Range, String), s.SymbolOccurrence]() + + // from is in tasty space, to in scala space + def checkIfTranslatableSymbol(from : String, to : String) : Boolean = { + if (from.isLocal != to.isLocal) { + false + } else { + if (from.isLocal) { + if(localTastyToScala.getOrElse(from, to) == to && + localScalaToTasty.getOrElse(to, from) == from) { + localTastyToScala += (from -> to) + localScalaToTasty += (to -> from) + true + } else { + false + } + } else { + true + } + } + } + + if (tastyOccurences.length != scalaOccurences.length) { + false + } else { + scalaOccurences.forall(occurence => { + if (symbols.contains(localScalaToTasty.getOrElse(occurence.symbol, occurence.symbol))) { + val siblings = symbols(occurence.symbol) + val nearest = siblings.minBy((c : s.SymbolOccurrence) => distance(occurence.range.get, lineToByte)(c.range.get)) + if (!checkIfTranslatableSymbol(nearest.symbol, occurence.symbol) || + translator.contains((nearest.range.get, nearest.symbol)) || + distance(occurence.range.get, lineToByte)(nearest.range.get) > 5) { + false + } else { + translator += ((nearest.range.get, nearest.symbol) -> occurence) + true + } + } else { + false + } + }) + } + } + // TODO: update scala-0.13 on version change (or resolve automatically) final def tastyClassDirectory = Paths.get("out/bootstrap/dotty-semanticdb/scala-0.12/test-classes/") @@ -34,9 +100,6 @@ class Tests { /** Returns the SemanticDB for this Scala source file. */ def getTastySemanticdb(classPath: Path, scalaFile: Path) : s.TextDocument = { val classNames = Utils.getClassNames(classPath, scalaFile, "example/") - println(classPath) - println(classNames) - println(scalaFile) val sdbconsumer = new SemanticdbConsumer(scalaFile) val _ = ConsumeTasty(classPath.toString, classNames, sdbconsumer) @@ -48,11 +111,10 @@ class Tests { val path = sourceDirectory.resolve(filename) val scalac = getScalacSemanticdb(path) val tasty = getTastySemanticdb(tastyClassDirectory, path) - println(tasty) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) - print("X=>",scalac.occurrences) - assertNoDiff(obtained, expected) + if (!compareOccurences(tasty.occurrences, scalac.occurrences, scalac.text)) + assertNoDiff(obtained, expected) } /** Fails the test with a pretty diff if there obtained is not the same as expected */ @@ -83,7 +145,7 @@ class Tests { } - /*@Test def testAccess(): Unit = checkFile("example/Access.scala") + @Test def testAccess(): Unit = checkFile("example/Access.scala") @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") @Test def testClasses(): Unit = checkFile("example/Classes.scala") @@ -119,12 +181,5 @@ class Tests { @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") - */ - //@Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols - //@Test def testNew(): Unit = checkFile("example/New.scala") - //@Test def testClasses(): Unit = checkFile("example/Classes.scala") - //@Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala") - @Test def testAccess(): Unit = checkFile("example/Access.scala") - //@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") } \ No newline at end of file From 44c055cb646e7ce04aa5325ef8cfd644afc3281f Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 21 Jan 2019 00:39:54 +0100 Subject: [PATCH 34/42] Improve test infrastructure and fix build errors: - automatic scala version inference in test suite - remove error message in test suite - implemented derived statements in classdef - fix bugs shown by rebase --- .../dotty/semanticdb/SemanticdbConsumer.scala | 41 ++++++++----------- semanticdb/src/dotty/semanticdb/Utils.scala | 15 ++++--- .../test/dotty/semanticdb/Semanticdbs.scala | 39 ++++++++---------- semanticdb/test/dotty/semanticdb/Tests.scala | 18 +++++--- 4 files changed, 55 insertions(+), 58 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 9015f85f2a0f..53c68d3b6d3f 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -61,13 +61,19 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } + def arePositionEqual(p1 : Position, p2 : Position) : Boolean = { + p1.start == p2.start && + p1.end == p2.end && + p1.sourceFile == p2.sourceFile + } + object Traverser extends TreeTraverser { implicit class TreeExtender(tree: Tree) { def isUserCreated: Boolean = { val children: List[Position] = ChildTraverser.getChildren(tree)(reflect.rootContext).map(_.pos) - return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children - .exists(_ == tree.pos)) + return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || + children.exists(arePositionEqual(tree.pos, _))) } } @@ -76,8 +82,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val children: List[Position] = ChildTraverser.getChildrenType(tree)(reflect.rootContext).collect(_ match { case IsTypeTree(tt) => tt.pos}) - return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || children - .exists(_ == tree.pos)) + return !((tree.pos.exists && tree.pos.start == tree.pos.end && children == Nil) || + children.exists(arePositionEqual(tree.pos, _))) } } @@ -457,7 +463,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { // dotty will generate a ValDef for the x, but the x will also // be present in the constructor, thus making a double definition if (symbolPathsMap.contains(key)) return - symbolPathsMap += key occurrences = occurrences :+ @@ -508,7 +513,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def addOccurencePatternTree(tree: Pattern, type_symbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - if (tree.isUserCreated) { + if (tree.symbol.isUseful && tree.isUserCreated) { addOccurence(tree.symbol, type_symbol, range) } } @@ -780,7 +785,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Apply(_, _) => { super.traverseTree(tree) } - case cl @ ClassDef(classname, constr, parents, selfopt, statements) => { + case ClassDef(classname, constr, parents, derived, selfopt, statements) => { // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -798,27 +803,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val rightmost = typesParameters.reverse.head.pos.end val end_ = nextCharacterSkipComments(sourceCode, rightmost) + 1 fittedInitClassRange = Some( - s.Range(tree.symbol.pos.startLine, + s.Range(0, end_, - tree.symbol.pos.startLine, + 0, end_)) } -/*s - if (!constr.isUserCreated) { - fittedInitClassRange = Some( - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1, - tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length + 1)) - } else { - fittedInitClassRange = Some( - s.Range(constr.symbol.pos.startLine, - constr.symbol.pos.startColumn, - constr.symbol.pos.endLine, - constr.symbol.pos.endColumn)) - }*/ - disableConstrParamTraversal = true traverseTree(constr) disableConstrParamTraversal = false @@ -865,6 +855,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case _ => } + derived.foreach(traverseTypeTree) + classStacks = tree.symbol :: classStacks val paramsPosMapping = generateParamsPosMapping(constr) @@ -1058,7 +1050,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } - Traverser.traverseTree(root)(reflect.rootContext) } diff --git a/semanticdb/src/dotty/semanticdb/Utils.scala b/semanticdb/src/dotty/semanticdb/Utils.scala index ede0044077e1..634e7ad9b80b 100644 --- a/semanticdb/src/dotty/semanticdb/Utils.scala +++ b/semanticdb/src/dotty/semanticdb/Utils.scala @@ -31,10 +31,10 @@ object Utils { /** List all tasty files occuring in the folder f or one of its subfolders */ def recursiveListFiles(f: File, prefix : String = ""): Array[File] = { - val pattern = (prefix + ".*\\.tasty").r + val pattern = (".*" + prefix + ".*\\.tasty").r val files = f.listFiles val folders = files.filter(_.isDirectory) - val tastyfiles = files.filter(_.toString match { + val tastyfiles = files.filter(_.toPath.toString match { case pattern(x: _*) => true case _ => false }) @@ -45,7 +45,7 @@ object Utils { def getTastyFiles(classPath: Path, prefix : String = ""): HashMap[String, List[Path]] = { val sourceToTasty: HashMap[String, List[Path]] = HashMap() val tastyfiles = recursiveListFiles(classPath.toFile(), prefix) - recursiveListFiles(classPath.toFile()).map(tastyPath => { + tastyfiles.map(tastyPath => { val (classpath, classname) = getClasspathClassname(tastyPath.toPath()) // We add an exception here to avoid crashing if we encountered // a bad tasty file @@ -58,7 +58,7 @@ object Utils { (source -> (tastyPath .toPath().toAbsolutePath :: sourceToTasty.getOrElse(source, Nil)))) } catch { - case _: InvocationTargetException => println(tastyPath) + case _: InvocationTargetException => () } }) sourceToTasty @@ -69,8 +69,13 @@ object Utils { extracted from the compilation artifacts found in [classPath]. */ def getClassNames(classPath: Path, scalaFile: Path, prefix : String = ""): List[String] = { + val tastyFiles = getTastyFiles(classPath.toAbsolutePath, prefix) + getClassNamesCached(scalaFile, tastyFiles) + } + + def getClassNamesCached(scalaFile: Path, allFiles : HashMap[String, List[Path]]): List[String] = { val tastyFiles = - getTastyFiles(classPath.toAbsolutePath, prefix) + allFiles .getOrElse(scalaFile.toString, Nil) val tastyClasses = tastyFiles.map(getClasspathClassname) diff --git a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala index d3617c601849..762673222ff5 100644 --- a/semanticdb/test/dotty/semanticdb/Semanticdbs.scala +++ b/semanticdb/test/dotty/semanticdb/Semanticdbs.scala @@ -81,8 +81,10 @@ object Semanticdbs { **/ def printTextDocument(doc: s.TextDocument): String = { val sb = new StringBuilder - val occurrences = doc.occurrences.sorted val sourceFile = SourceFile.virtual(doc.uri, doc.text) + implicit val occurrenceOrdering: Ordering[s.SymbolOccurrence] = + buildOccurrenceOrdering(sourceFile) + val occurrences = doc.occurrences.sorted var offset = 0 occurrences.foreach { occ => val range = occ.range.get @@ -98,35 +100,28 @@ object Semanticdbs { } /** Sort symbol occurrences by their start position. */ - implicit val occurrenceOrdering: Ordering[s.SymbolOccurrence] = + def buildOccurrenceOrdering(sourceFile: SourceFile): Ordering[s.SymbolOccurrence] = { new Ordering[s.SymbolOccurrence] { + def rangeToTuple(r : s.Range): (Int, Int) = { + val start = sourceFile.lineToOffset(r.startLine) + r.startCharacter + val end = sourceFile.lineToOffset(r.endLine) + r.endCharacter + (start, end) + } + override def compare(x: s.SymbolOccurrence, y: s.SymbolOccurrence): Int = { if (x.range.isEmpty) 0 else if (y.range.isEmpty) 0 else { - val a = x.range.get - val b = y.range.get - val byLine = Integer.compare( - a.startLine, - b.startLine - ) - if (byLine != 0) { - byLine + val (as, ae) = rangeToTuple(x.range.get) + val (bs, be) = rangeToTuple(y.range.get) + val byStart = Integer.compare(as, bs) + if (byStart != 0) { + byStart } else { - val byCharacter = Integer.compare( - a.startCharacter, - b.startCharacter - ) - if (byCharacter == 0) { - Integer.compare( - a.endCharacter, - b.endCharacter - ) - } else { - byCharacter - } + Integer.compare(ae, be) } } } } + } } diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index aaaf7dbca74d..4b5f7561d95d 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -82,9 +82,15 @@ class Tests { } } - // TODO: update scala-0.13 on version change (or resolve automatically) - final def tastyClassDirectory = - Paths.get("out/bootstrap/dotty-semanticdb/scala-0.12/test-classes/") + final def tastyClassDirectory = { + val root = "out/bootstrap/dotty-semanticdb/" + val files = Paths.get(root).toFile().listFiles + val scalaFolderReg = """scala-(\d+)\.(\d+)""".r + val (_, _, path) = files.collect(file => file.getName match { + case scalaFolderReg(major, minor) => (major, minor, file.getName) + }).max + Paths.get(root, path, "test-classes") + } val sourceroot = Paths.get("semanticdb/input").toAbsolutePath val sourceDirectory = sourceroot.resolve("src/main/scala") @@ -97,9 +103,11 @@ class Tests { semanticdbLoader.resolve(scalaFile).get } + final def allTastyFiles = Utils.getTastyFiles(tastyClassDirectory, "example") + /** Returns the SemanticDB for this Scala source file. */ def getTastySemanticdb(classPath: Path, scalaFile: Path) : s.TextDocument = { - val classNames = Utils.getClassNames(classPath, scalaFile, "example/") + val classNames = Utils.getClassNamesCached(scalaFile, allTastyFiles) val sdbconsumer = new SemanticdbConsumer(scalaFile) val _ = ConsumeTasty(classPath.toString, classNames, sdbconsumer) @@ -144,7 +152,6 @@ class Tests { } } - @Test def testAccess(): Unit = checkFile("example/Access.scala") @Test def testAdvanced(): Unit = checkFile("example/Advanced.scala") @Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala") @@ -181,5 +188,4 @@ class Tests { @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") - } \ No newline at end of file From fe9df6f01190d1937ae4184d0f8cb68203e43097 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 21 Jan 2019 02:15:45 +0100 Subject: [PATCH 35/42] Refactor onfly source code parsing and clean up ranges creation --- .../dotty/semanticdb/SemanticdbConsumer.scala | 348 +++++++----------- .../src/dotty/semanticdb/SourceFile.scala | 57 +++ semanticdb/test/dotty/semanticdb/Tests.scala | 45 ++- 3 files changed, 221 insertions(+), 229 deletions(-) create mode 100644 semanticdb/src/dotty/semanticdb/SourceFile.scala diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 53c68d3b6d3f..50eef7d36700 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -11,22 +11,21 @@ import scala.meta.internal.{semanticdb => s} import dotty.semanticdb.Scala.{Descriptor => d} import dotty.semanticdb.Scala._ -import scala.io.Source -class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { +class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsumer { var stack: List[String] = Nil val semantic: s.TextDocument = s.TextDocument() var occurrences: Seq[s.SymbolOccurrence] = Seq() def toSemanticdb(): s.TextDocument = { - s.TextDocument(text = sourceCode, occurrences = occurrences) + s.TextDocument(text = sourceCode.content(), occurrences = occurrences) } val package_definitions: Set[Tuple2[String, Int]] = Set() val symbolsCache: HashMap[(Any, s.Range), String] = HashMap() var local_offset: Int = 0 - val sourceCode = Source.fromFile(sourceFile.toFile).mkString + val sourceCode = new SourceFile(sourceFilePath) final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -94,6 +93,23 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } implicit class SymbolExtender(symbol: Symbol) { + /* Return true if symbol represents the definition of a var setter, false otherwise. + We return true if the extract of source code corresponding to the position of the symbol is the same as the symbol name. + Ex: + var m = ??? + -> there is a defdef for `m_=` with position "m =". As "m =" != "m_=", we return false + */ + def isMutableSetterExplicit(role : s.SymbolOccurrence.Role) = { + if (role == s.SymbolOccurrence.Role.DEFINITION && + symbol.pos.exists && + symbol.flags.is(Flags.Mutable) && symbol.isMethod && + symbol.trueName.endsWith("_=")) + (sourceCode.peek(symbol.pos.start, symbol.pos.end) == symbol.trueName) + else + true + } + + def trueName: String = { val prohibitedChars = '.' :: ';' :: '[' :: '/' :: '<' :: '>' :: Nil //val prohibitedHashMap = prohibitedChars.map(x => x -> "$u%04X".format(x.toInt)).toMap @@ -352,15 +368,13 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { def iterateParent(symbol: Symbol, isMutableAssignement:Boolean=false): String = { if (symbol.name == "" || symbol.name == "") then { - // TODO had a "NoDenotation" test to avoid - // relying on the name itself "" } else { val previous_symbol = /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. This is illogic: a init method can't have any child. Thus, when the current symbol is - a typeparameter (or anything), and the owner is an init, we can just "jump" over the init. */ + a typeparameter, and the owner is an init, we can just "jump" over the init. */ if (symbol.owner.name == "" && symbol.isType) iterateParent(symbol.owner.owner) else @@ -398,28 +412,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } - def addOccurencePredef(parent: String, name: String, range: s.Range): Unit = { - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - parent + name + "().", - s.SymbolOccurrence.Role.DEFINITION - ) - } - def addSelfDefinition(name: String, range: s.Range): Unit = { - var localsymbol = Symbols.Local(local_offset.toString) - local_offset += 1 - symbolsCache += ((name, range) -> localsymbol) - occurrences = - occurrences :+ - s.SymbolOccurrence( - Some(range), - localsymbol, - s.SymbolOccurrence.Role.DEFINITION - ) - } - def symbolToSymbolString(symbol: Symbol, isMutableAssignement:Boolean = false): (String, Boolean) = { if (symbol.isSemanticdbLocal) { var localsymbol = Symbols.Local(local_offset.toString) @@ -437,69 +429,70 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (symbol.name == "") return val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName - val (symbol_path, is_global) = posToRange(symbol.pos) match { - case Some(keyRange) - if symbolsCache.contains((symbolName, keyRange)) => { - (symbolsCache((symbolName, keyRange)), symbol.isSemanticdbLocal)} - case Some(keyRange) => { - val (sp, ig) = symbolToSymbolString(symbol, isMutableAssignement) - symbolsCache += ((symbolName, keyRange) -> sp) - (sp, ig) - } - case _ => + val (symbol_path, is_global) = + if (symbol.pos.exists) { + val keyRange = createRange(symbol.pos) + if (symbolsCache.contains((symbolName, keyRange))) + (symbolsCache((symbolName, keyRange)), symbol.isSemanticdbLocal) + else { + val (sp, ig) = symbolToSymbolString(symbol, isMutableAssignement) + symbolsCache += ((symbolName, keyRange) -> sp) + (sp, ig) + } + } else { symbolToSymbolString(symbol) - } + } - // We want to add symbols coming from our file - // if (symbol.pos.sourceFile != sourceFile) return - if (symbol_path == "" /*|| symbol.isUselessOccurrence*/) return + if (symbol_path == "") return if (symbol.flags.is(Flags.Synthetic) && type_symbol == s.SymbolOccurrence.Role.DEFINITION) return - val key = (symbol_path, range) - // TODO: refactor the following - - // this is to avoid duplicates symbols + val key = (symbol_path, range) // this is to avoid duplicates symbols // For example, when we define a class as: `class foo(x: Int)`, // dotty will generate a ValDef for the x, but the x will also // be present in the constructor, thus making a double definition - if (symbolPathsMap.contains(key)) return - symbolPathsMap += key + if (!symbolPathsMap.contains(key)) { + symbolPathsMap += key + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + symbol_path, + type_symbol + ) + } + } + + def addOccurencePredef(parent: String, name: String, range: s.Range): Unit = { occurrences = occurrences :+ s.SymbolOccurrence( Some(range), - symbol_path, - type_symbol + parent + name + "().", + s.SymbolOccurrence.Role.DEFINITION ) } - - /* Return true if symbol represents the definition of a var setter, false otherwise. - We return true if the extract of source code corresponding to the position of the symbol is the same as the symbol name. - Ex: - var m = ??? - -> there is a defdef for `m_=` with position "m =". As "m =" != "m_=", we return false - */ - def isMutableSetterExplicit(symbol : Symbol, role : s.SymbolOccurrence.Role) = { - if (role == s.SymbolOccurrence.Role.DEFINITION && - symbol.pos.exists && - symbol.flags.is(Flags.Mutable) && symbol.isMethod && - symbol.trueName.endsWith("_=")) - (sourceCode.substring(symbol.pos.start, symbol.pos.end) == symbol.trueName) - else - true + def addSelfDefinition(name: String, range: s.Range): Unit = { + var localsymbol = Symbols.Local(local_offset.toString) + local_offset += 1 + symbolsCache += ((name, range) -> localsymbol) + occurrences = + occurrences :+ + s.SymbolOccurrence( + Some(range), + localsymbol, + s.SymbolOccurrence.Role.DEFINITION + ) } - val reservedFunctions: List[String] = Nil def addOccurenceTree(tree: Tree, typeSymbol: s.SymbolOccurrence.Role, range: s.Range, force_add: Boolean = false, isMutableAssignement: Boolean = false): Unit = { if (tree.symbol.isUseful && - isMutableSetterExplicit(tree.symbol, typeSymbol) && - (tree.isUserCreated || - (force_add /*&& !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#``().")*/))) { + tree.symbol.isMutableSetterExplicit(typeSymbol) && + (tree.isUserCreated || force_add)) { addOccurence(tree.symbol, typeSymbol, range, isMutableAssignement) } } @@ -517,56 +510,48 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { addOccurence(tree.symbol, type_symbol, range) } } + def addOccurenceId(parent_path: String, id: Id): Unit = { val symbol_path = Symbols.Global(parent_path, d.Term(id.name)) occurrences = occurrences :+ s.SymbolOccurrence( - Some( - s.Range(id.pos.startLine, - id.pos.startColumn, - id.pos.startLine, - id.pos.endColumn)), + Some(createRange(id.pos)), symbol_path, s.SymbolOccurrence.Role.REFERENCE ) } - def posToRange(pos: Position): Option[s.Range] = { - if (pos.exists) { - Some( - s.Range(pos.startLine, - pos.startColumn, - pos.startLine, - pos.endColumn)) - } else { - None - } - } + def createRange(pos: Position): s.Range = + createRange(pos.startLine, pos.startColumn, pos.endLine, pos.endColumn) - def range(tree: Tree, pos: Position, name: String): s.Range = { - val offset = tree match { - case IsPackageClause(tree) => "package ".length - case IsClassDef(tree) if tree.symbol.flags.is(Flags.Object) => -1 - case _ => 0 - } + def createRange(startLine : Int, startColumn : Int, length : Int) : s.Range = { + createRange(startLine, startColumn, startLine, startColumn + length) + } - val range_end_column = - if (name == "") { - pos.endColumn + def createRange(startLine: Int, startColumn: Int, endLine: Int, endColumn: Int): s.Range = { + def aux(l : Int, c : Int) : (Int, Int) = { + if (l == 0) { + val line = sourceCode.offsetToLine(l) + (line, c - sourceCode.lineToOffset(line)) } else { - pos.startColumn + name.length + (l, c) } + } - s.Range(pos.startLine, - pos.startColumn + offset, - pos.startLine, - range_end_column + offset) + val (sl, sc) = aux(startLine, startColumn) + val (el, ec) = aux(endLine, endColumn) + s.Range(sl, sc, el, ec) + } + + /* Create a "point range" (a range refering to a point position) */ + def createRange(line: Int, column: Int) : s.Range = { + createRange(line, column, line, column) } def rangeSelect(name: String, range: Position): s.Range = { if (name == "") { - return posToRange(range).get + return createRange(range) } else /* The position of a select is the position of the whole select expression, from the start to the end. @@ -578,36 +563,26 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { Ex: A + B -> the position of the select is the range "A +", so we pick the range "+" */ if (name.endsWith(":")) { - return s.Range(range.startLine, - range.startColumn, - range.startLine, - range.startColumn + name.length) + return createRange(range.startLine, range.startColumn, name.length) } else { - return s.Range(range.endLine, - range.endColumn - name.length, - range.endLine, - range.endColumn) + return createRange(range.endLine, range.endColumn - name.length, name.length) } } - def getImportPath(path_term: Term): String = { - path_term match { + def getImportPath(pathTerm: Term): String = { + val range = pathTerm match { case Term.Select(qualifier, selected) => { getImportPath(qualifier) - val range = rangeSelect(selected, path_term.pos) - addOccurenceTree(path_term, - s.SymbolOccurrence.Role.REFERENCE, - range) - iterateParent(path_term.symbol) + rangeSelect(selected, pathTerm.pos) } case Term.Ident(x) => { - val range_x = range(path_term, path_term.pos, path_term.symbol.trueName) - addOccurenceTree(path_term, - s.SymbolOccurrence.Role.REFERENCE, - range_x) - iterateParent(path_term.symbol) + createRange(pathTerm.pos.startLine, pathTerm.pos.startColumn, pathTerm.symbol.trueName.length) } } + addOccurenceTree(pathTerm, + s.SymbolOccurrence.Role.REFERENCE, + range) + iterateParent(pathTerm.symbol) } def getImportSelectors(parent_path: String, @@ -638,10 +613,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val typetree = extractTypeTree(tree) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, - s.Range(typetree.pos.startLine, - typetree.pos.startColumn, - typetree.pos.startLine, - typetree.pos.endColumn)) + createRange(typetree.pos)) } case TypeTree.Select(qualifier, _) => { val typetree = extractTypeTree(tree) @@ -686,11 +658,10 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val typetree = extractTypeTree(tree) val start = typetree.pos.start val end = typetree.pos.end - if (end < sourceCode.length - && sourceCode.substring(start, end) == typetree.symbol.name) { + if (sourceCode.peek(start, end) == typetree.symbol.name) { addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, - posToRange(typetree.pos).get) + createRange(typetree.pos)) } } @@ -706,10 +677,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { addOccurencePatternTree( tree, s.SymbolOccurrence.Role.REFERENCE, - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn, - tree.symbol.pos.endLine, - tree.symbol.pos.startColumn + name.length) + createRange(tree.symbol.pos.startLine, tree.symbol.pos.startColumn, name.length) ) super.traversePattern(tree) } @@ -730,32 +698,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } } - def nextCharacterSkipComments(s : String, start : Int): Int = { - def aux(start : Int) : Int = { - var i = start - if (i+2 <= s.length && s.substring(i, i+2) == "//") { - while (i < s.length && s(i) != '\n') - i += 1 - return i+1 - } else if (i+2 <= s.length && s.substring(i, i+2) == "/*") { - while (i + 2 <= s.length && s.substring(i, i+2) != "*/") - i += 1 - return i+2 - } else { - while (i < s.length && s(i).isWhitespace) - i += 1 - return i - } - } - var previous = start - var next = aux(previous) - while (previous != next) { - previous = next - next = aux(previous) - } - return previous - } - var disableConstrParamTraversal = false def generateParamsPosMapping(cdef: DefDef)(implicit ctx: Context): Map[String, s.Range] = { @@ -763,7 +705,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { val start = Map[String, s.Range]() params.foldLeft(start)((old, statements) => { statements.foldLeft(old)((old, cval) => { - old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName)) + old + (cval.name -> createRange(cval.symbol.pos.startLine, cval.symbol.pos.startColumn, cval.symbol.trueName.length)) }) } ) @@ -786,27 +728,26 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { super.traverseTree(tree) } case ClassDef(classname, constr, parents, derived, selfopt, statements) => { + + + val offsetSymbolClass = + if(tree.symbol.flags.is(Flags.Object)) -1 + else 0 // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.symbol.pos, tree.symbol.trueName)) + createRange(tree.symbol.pos.startLine, tree.symbol.pos.startColumn + offsetSymbolClass, tree.symbol.trueName.length)) // then the constructor val DefDef(_, typesParameters, _, _, _) = constr if (typesParameters.isEmpty) { fittedInitClassRange = Some( - s.Range(tree.symbol.pos.startLine, - tree.symbol.pos.startColumn + classname.length, - tree.symbol.pos.startLine, + createRange(tree.symbol.pos.startLine, tree.symbol.pos.startColumn + classname.length)) } else { val rightmost = typesParameters.reverse.head.pos.end - val end_ = nextCharacterSkipComments(sourceCode, rightmost) + 1 - fittedInitClassRange = Some( - s.Range(0, - end_, - 0, - end_)) + val end_ = sourceCode.nextCharacterSkipComments(rightmost) + 1 + fittedInitClassRange = Some(createRange(0, end_)) } disableConstrParamTraversal = true @@ -840,15 +781,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case IsTerm(t) if t.pos.end < old => t.pos.end case _ => old }) - var posColumn = sourceCode.indexOf("{", if (startPosSearch == tree.pos.end) tree.pos.start else startPosSearch) - while (posColumn < sourceCode.length && !sourceCode(posColumn).isLetter) posColumn += 1 + var posColumn = if (startPosSearch == tree.pos.end) tree.pos.start else startPosSearch + posColumn = sourceCode.firstOccurrenceLetter('{', posColumn) + posColumn = sourceCode.nextCharacterSkipComments(posColumn+1) - addSelfDefinition(name, - s.Range(0, - posColumn, - 0, - posColumn + name.length)) + addSelfDefinition(name, createRange(0, posColumn, name.length)) } traverseTypeTree(type_) } @@ -887,6 +825,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { typeparams.foreach(traverseTree) } + case DefDef("", _, _, _, _) if tree.symbol.owner.flags.is(Flags.Object) => { + } + case Term.Assign(lhs, rhs) => { isAssignedTerm = true traverseTree(lhs) @@ -902,10 +843,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { addOccurence( within.typeSymbol, s.SymbolOccurrence.Role.REFERENCE, - s.Range(cdef.pos.startLine, - startColumn, - cdef.pos.startLine, - startColumn + within.typeSymbol.trueName.length) + createRange(cdef.pos.startLine, startColumn, within.typeSymbol.trueName.length) ) } case _ => @@ -917,10 +855,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { addOccurence( within.typeSymbol, s.SymbolOccurrence.Role.REFERENCE, - s.Range(cdef.pos.startLine, - startColumn, - cdef.pos.startLine, - startColumn + within.typeSymbol.trueName.length) + createRange(cdef.pos.startLine, startColumn, within.typeSymbol.trueName.length) ) } case _ => @@ -928,24 +863,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { } if (tree.symbol.trueName != "") { val pos = tree.symbol.pos - var rangeSymbol = range(tree, pos, tree.symbol.trueName) + var rangeSymbol = createRange(pos.startLine, pos.startColumn, tree.symbol.trueName.length) - if (tree.symbol.trueName == "" && pos.start + 4 < sourceCode.length - && sourceCode.substring(pos.start, pos.start + 4) == "this") { - rangeSymbol = s.Range(pos.startLine, pos.startColumn, pos.startLine, pos.startColumn + 4) + if (tree.symbol.trueName == "" && sourceCode.peek(pos.start, pos.start + 4) == "this") { + rangeSymbol = createRange(pos.startLine, pos.startColumn, pos.startColumn + 4) } - - if (tree.symbol.trueName == "" && tree.symbol.owner != NoSymbol && tree.symbol.owner.flags.is(Flags.Object)) { - } else if (tree.symbol.trueName == "" && fittedInitClassRange != None) { - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - fittedInitClassRange.get, - true) - } else { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, rangeSymbol) - } + } //if (!disableConstrParamTraversal) super.traverseTree(cdef) @@ -959,9 +885,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { Distinguishing between the two is easy. If the sourcecode between [pos.start; pos.end] ends with a 'this', then we're in the first case, otherwise the second */ - var rangeThis = posToRange(tree.pos).get - if (sourceCode.substring(tree.pos.start, tree.pos.end).endsWith("this")) { - rangeThis = range(tree, tree.pos, tree.symbol.trueName) + var rangeThis = createRange(tree.pos) + if (sourceCode.peek(tree.pos.start, tree.pos.end).endsWith("this")) { + rangeThis = createRange(tree.pos.startLine, tree.pos.startColumn, tree.symbol.trueName.length) } /*range = s.Range(tree.pos.startLine, tree.pos.startColumn, tree.pos.endLine, @@ -975,7 +901,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { { addOccurence(classStacks.head, s.SymbolOccurrence.Role.DEFINITION, - posToRange(id.pos).get) + createRange(id.pos)) super.traverseTree(tree) } @@ -984,11 +910,11 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { var shouldForceAdd = false if (tree.symbol.trueName == "") { - if (tree.pos.start == tree.pos.end && 5 <= tree.pos.start && sourceCode.substring(tree.pos.start - 5, tree.pos.start - 1) == "this") { - range = s.Range(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, tree.pos.start - 1) + if (tree.pos.start == tree.pos.end && sourceCode.peek(tree.pos.start - 5, tree.pos.start - 1) == "this") { + range = createRange(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, 4) shouldForceAdd = true } else { - range = s.Range(tree.pos.endLine, tree.pos.endColumn, tree.pos.endLine, tree.pos.endColumn) + range = createRange(tree.pos.endLine, tree.pos.endColumn, tree.pos.endLine, tree.pos.endColumn) shouldForceAdd = qualifier.isUserCreated } } @@ -1002,7 +928,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case Term.Ident(name) => { addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, - range(tree, tree.pos, tree.symbol.trueName)) + createRange(tree.pos.startLine, tree.pos.startColumn, tree.symbol.trueName.length)) super.traverseTree(tree) } @@ -1017,17 +943,17 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { case IsTypeTree(t) => t.symbol } def getPredefFunction(pos: Int): String = { - sourceCode(pos) match { - case 'l' => "locally" - case 'i' => "implicitly" - case 'a' => "assert" + sourceCode.peek(pos, pos+1) match { + case "l" => "locally" + case "i" => "implicitly" + case "a" => "assert" } } val parentSymbol = iterateParent(extractSymbol(c)) if (parentSymbol == "dotty/DottyPredef.") { val pos = extractPos(c) val function = getPredefFunction(pos.start) - val range = s.Range(pos.startLine, pos.startColumn, pos.startLine, pos.startColumn + function.length) + val range = createRange(pos.startLine, pos.startColumn, pos.startLine, function.length) addOccurencePredef(parentSymbol, function, range) } @@ -1039,7 +965,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer { if (!package_definitions(key)) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, - range(tree, tree.pos, tree.symbol.trueName)) + createRange(tree.pos.startLine, tree.pos.startColumn + "package ".length, tree.symbol.trueName.length)) package_definitions += key } super.traverseTree(tree) diff --git a/semanticdb/src/dotty/semanticdb/SourceFile.scala b/semanticdb/src/dotty/semanticdb/SourceFile.scala new file mode 100644 index 000000000000..15aa2a691bf4 --- /dev/null +++ b/semanticdb/src/dotty/semanticdb/SourceFile.scala @@ -0,0 +1,57 @@ +package dotty.semanticdb + +import scala.io.Source +import scala.math._ +import dotty.tools.dotc.util.SourceFile + +class SourceFile(path: java.nio.file.Path) { + private val sourceCode = Source.fromFile(path.toFile).mkString + private val sourceFile = SourceFile.virtual(path.toString, sourceCode) + + def content() = sourceCode + + def offsetToLine(offset: Int): Int = sourceFile.offsetToLine(offset) + + def lineToOffset(offset: Int): Int = sourceFile.lineToOffset(offset) + + def peek(start: Int, end: Int) : String = + sourceCode.substring(max(start, 0), min(end, sourceCode.length - 1)) + + def firstOccurrenceLetter(letter: Char, start : Int) : Int = { + var pos = start + while (pos < sourceCode.length && sourceCode(pos) != letter) { + val nextPos = nextCharacterSkipComments(pos) + if (nextPos == pos) + pos = nextPos + 1 + else + pos = nextPos + } + return pos + } + + def nextCharacterSkipComments(start : Int): Int = { + def aux(start : Int) : Int = { + var i = start + if (i+2 <= sourceCode.length && sourceCode.substring(i, i+2) == "//") { + while (i < sourceCode.length && sourceCode(i) != '\n') + i += 1 + return i+1 + } else if (i+2 <= sourceCode.length && sourceCode.substring(i, i+2) == "/*") { + while (i + 2 <= sourceCode.length && sourceCode.substring(i, i+2) != "*/") + i += 1 + return i+2 + } else { + while (i < sourceCode.length && sourceCode(i).isWhitespace) + i += 1 + return i + } + } + var previous = start + var next = aux(previous) + while (previous != next) { + previous = next + next = aux(previous) + } + return previous + } +} \ No newline at end of file diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 4b5f7561d95d..f44d27ce7a09 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -15,6 +15,7 @@ import scala.tasty.file.TastyConsumer import java.lang.reflect.InvocationTargetException import dotty.semanticdb.Scala._ +import dotty.tools.dotc.util.SourceFile class Tests { @@ -22,20 +23,20 @@ class Tests { def min(a: Int, b: Int) : Int = if (a > b) b else a def max(a: Int, b: Int) : Int = if (a > b) a else b def abs(a: Int, b: Int) : Int = max(a, b) - min(a, b) - def distance(r1 : s.Range, offsets : Array[Int])(r2 : s.Range) : Int = { - val s1 = offsets(max(r1.startLine, 0)) + r1.startCharacter - val s2 = offsets(max(r2.startLine, 0)) + r2.startCharacter - val e1 = offsets(max(r1.endLine, 0)) + r1.endCharacter - val e2 = offsets(max(r2.endLine, 0)) + r2.endCharacter + def distance(r1 : s.Range, sourceFile : SourceFile)(r2 : s.Range) : Int = { + val s1 = sourceFile.lineToOffset(max(r1.startLine, 0)) + r1.startCharacter + val s2 = sourceFile.lineToOffset(max(r2.startLine, 0)) + r2.startCharacter + val e1 = sourceFile.lineToOffset(max(r1.endLine, 0)) + r1.endCharacter + val e2 = sourceFile.lineToOffset(max(r2.endLine, 0)) + r2.endCharacter max(abs(s1, s2), abs(e1, e2)) } - def compareOccurences(tastyOccurences : Seq[s.SymbolOccurrence], - scalaOccurences : Seq[s.SymbolOccurrence], + def compareOccurrences(tastyOccurrences : Seq[s.SymbolOccurrence], + scalaOccurrences : Seq[s.SymbolOccurrence], sourceCode : String) : Boolean= { - val lineToByte = sourceCode.split("\n").scanLeft(0)((o, l) => o + l.length + 1) - val symbols = tastyOccurences.groupBy(_.symbol) + val sourceFile = SourceFile.virtual("", sourceCode) + val symbols = tastyOccurrences.groupBy(_.symbol) val localTastyToScala = HashMap[String, String]() val localScalaToTasty = HashMap[String, String]() val translator = HashMap[(s.Range, String), s.SymbolOccurrence]() @@ -60,19 +61,26 @@ class Tests { } } - if (tastyOccurences.length != scalaOccurences.length) { + if (tastyOccurrences.length != scalaOccurrences.length) { false } else { - scalaOccurences.forall(occurence => { - if (symbols.contains(localScalaToTasty.getOrElse(occurence.symbol, occurence.symbol))) { - val siblings = symbols(occurence.symbol) - val nearest = siblings.minBy((c : s.SymbolOccurrence) => distance(occurence.range.get, lineToByte)(c.range.get)) - if (!checkIfTranslatableSymbol(nearest.symbol, occurence.symbol) || + scalaOccurrences.forall(occurrence => { + if (occurrence.symbol.isLocal || + symbols.contains(localScalaToTasty.getOrElse(occurrence.symbol, occurrence.symbol))) { + val siblings = + if (occurrence.symbol.isLocal) tastyOccurrences + else symbols(occurrence.symbol) + + val nearest = siblings.minBy(c => distance(occurrence.range.get, sourceFile)(c.range.get)) + + if (!checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol) || translator.contains((nearest.range.get, nearest.symbol)) || - distance(occurence.range.get, lineToByte)(nearest.range.get) > 5) { + distance(occurrence.range.get, sourceFile)(nearest.range.get) > 5) { + println(checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol)) false } else { - translator += ((nearest.range.get, nearest.symbol) -> occurence) + if (!occurrence.symbol.isLocal) + translator += ((nearest.range.get, nearest.symbol) -> occurrence) true } } else { @@ -121,7 +129,7 @@ class Tests { val tasty = getTastySemanticdb(tastyClassDirectory, path) val obtained = Semanticdbs.printTextDocument(tasty) val expected = Semanticdbs.printTextDocument(scalac) - if (!compareOccurences(tasty.occurrences, scalac.occurrences, scalac.text)) + if (!compareOccurrences(tasty.occurrences, scalac.occurrences, scalac.text)) assertNoDiff(obtained, expected) } @@ -188,4 +196,5 @@ class Tests { @Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala") @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") + } \ No newline at end of file From 83b024696d33cffc31fa155279e141e15a10a8c4 Mon Sep 17 00:00:00 2001 From: poechsel Date: Mon, 21 Jan 2019 12:37:46 +0100 Subject: [PATCH 36/42] Add documentation --- .../dotty/semanticdb/SemanticdbConsumer.scala | 261 +++++++++++------- semanticdb/test/dotty/semanticdb/Tests.scala | 145 ++++++---- 2 files changed, 249 insertions(+), 157 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 50eef7d36700..ebc5f24b7d4e 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -21,15 +21,21 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def toSemanticdb(): s.TextDocument = { s.TextDocument(text = sourceCode.content(), occurrences = occurrences) } - val package_definitions: Set[Tuple2[String, Int]] = Set() + + // Caching for package definitions (as they are shared accross different class files) + val packageDefinitions: Set[(String, Int)] = Set() + // Caching for symbol paths to avoid regenerating some of them + // (as computing a symbol path from a symbol is deterministic) val symbolsCache: HashMap[(Any, s.Range), String] = HashMap() - var local_offset: Int = 0 + // Offset for local symbol + var localOffset: Int = 0 val sourceCode = new SourceFile(sourceFilePath) final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ + // To avoid adding symbol paths duplicates inside a same class val symbolPathsMap: Set[(String, s.Range)] = Set() object ChildTraverser extends TreeTraverser { @@ -60,6 +66,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } + /* The (==) operator does not work correctly on two positions, + we redefine our one */ def arePositionEqual(p1 : Position, p2 : Position) : Boolean = { p1.start == p2.start && p1.end == p2.end && @@ -86,6 +94,24 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } + implicit class TermOrTypeTreeExtender(tree: TermOrTypeTree) { + def pos: Position = tree match { + case IsTerm(t) => t.pos + case IsTypeTree(t) => t.pos + } + + def symbol: Symbol = tree match { + case IsTerm(t) => t.symbol + case IsTypeTree(t) => t.symbol + } + } + + implicit class TypeOrBoundsTreeExtender(tree: TypeOrBoundsTree) { + def typetree: TypeTree = tree match { + case IsTypeTree(t) => t + } + } + implicit class PatternExtender(tree: Pattern) { def isUserCreated: Boolean = { return !(tree.pos.exists && tree.pos.start == tree.pos.end) @@ -109,13 +135,12 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum true } - + // The name of a symbol can contain special chars. This will replace them with the correct char. def trueName: String = { - val prohibitedChars = '.' :: ';' :: '[' :: '/' :: '<' :: '>' :: Nil - //val prohibitedHashMap = prohibitedChars.map(x => x -> "$u%04X".format(x.toInt)).toMap - prohibitedChars.foldLeft(symbol.name)((old, chr) => - old.replaceAll("\\$u%04X".format(chr.toInt), chr.toString) - ) + val prohibitedChars = '.' :: ';' :: '[' :: '/' :: '<' :: '>' :: Nil + prohibitedChars.foldLeft(symbol.name)((old, chr) => + old.replaceAll("\\$u%04X".format(chr.toInt), chr.toString) + ) } @@ -336,7 +361,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum case _ => symbol } - def disimbiguate(symbol_path: String, symbol: Symbol): String = { + def disimbiguate(symbolPath: String, symbol: Symbol): String = { try { val symbolcl = resolveClass(symbol.owner.asClass) symbolcl match { @@ -414,8 +439,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def symbolToSymbolString(symbol: Symbol, isMutableAssignement:Boolean = false): (String, Boolean) = { if (symbol.isSemanticdbLocal) { - var localsymbol = Symbols.Local(local_offset.toString) - local_offset += 1 + var localsymbol = Symbols.Local(localOffset.toString) + localOffset += 1 (localsymbol, false) } else { (iterateParent(symbol, isMutableAssignement), true) @@ -423,13 +448,13 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def addOccurence(symbol: Symbol, - type_symbol: s.SymbolOccurrence.Role, + typeSymbol: s.SymbolOccurrence.Role, range: s.Range, isMutableAssignement:Boolean = false): Unit = { if (symbol.name == "") return val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName - val (symbol_path, is_global) = + val (symbolPath, isGlobal) = if (symbol.pos.exists) { val keyRange = createRange(symbol.pos) if (symbolsCache.contains((symbolName, keyRange))) @@ -443,10 +468,10 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum symbolToSymbolString(symbol) } - if (symbol_path == "") return - if (symbol.flags.is(Flags.Synthetic) && type_symbol == s.SymbolOccurrence.Role.DEFINITION) return + if (symbolPath == "") return + if (symbol.flags.is(Flags.Synthetic) && typeSymbol == s.SymbolOccurrence.Role.DEFINITION) return - val key = (symbol_path, range) // this is to avoid duplicates symbols + val key = (symbolPath, range) // this is to avoid duplicates symbols // For example, when we define a class as: `class foo(x: Int)`, // dotty will generate a ValDef for the x, but the x will also // be present in the constructor, thus making a double definition @@ -456,8 +481,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum occurrences :+ s.SymbolOccurrence( Some(range), - symbol_path, - type_symbol + symbolPath, + typeSymbol ) } } @@ -473,8 +498,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def addSelfDefinition(name: String, range: s.Range): Unit = { - var localsymbol = Symbols.Local(local_offset.toString) - local_offset += 1 + var localsymbol = Symbols.Local(localOffset.toString) + localOffset += 1 symbolsCache += ((name, range) -> localsymbol) occurrences = occurrences :+ @@ -488,36 +513,38 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def addOccurenceTree(tree: Tree, typeSymbol: s.SymbolOccurrence.Role, range: s.Range, - force_add: Boolean = false, + forceAdd: Boolean = false, isMutableAssignement: Boolean = false): Unit = { if (tree.symbol.isUseful && tree.symbol.isMutableSetterExplicit(typeSymbol) && - (tree.isUserCreated || force_add)) { + (tree.isUserCreated || forceAdd)) { addOccurence(tree.symbol, typeSymbol, range, isMutableAssignement) } } + def addOccurenceTypeTree(typetree: TypeTree, - type_symbol: s.SymbolOccurrence.Role, + typeSymbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { if (typetree.symbol.isUseful && typetree.isUserCreated) { - addOccurence(typetree.symbol, type_symbol, range) + addOccurence(typetree.symbol, typeSymbol, range) } } + def addOccurencePatternTree(tree: Pattern, - type_symbol: s.SymbolOccurrence.Role, + typeSymbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { if (tree.symbol.isUseful && tree.isUserCreated) { - addOccurence(tree.symbol, type_symbol, range) + addOccurence(tree.symbol, typeSymbol, range) } } - def addOccurenceId(parent_path: String, id: Id): Unit = { - val symbol_path = Symbols.Global(parent_path, d.Term(id.name)) + def addOccurenceId(parentPath: String, id: Id): Unit = { + val symbolPath = Symbols.Global(parentPath, d.Term(id.name)) occurrences = occurrences :+ s.SymbolOccurrence( Some(createRange(id.pos)), - symbol_path, + symbolPath, s.SymbolOccurrence.Role.REFERENCE ) } @@ -530,6 +557,9 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def createRange(startLine: Int, startColumn: Int, endLine: Int, endColumn: Int): s.Range = { + /* This aux function is to make sure every generated range are coherent, + meaning they all have a valid startLine and startColumn (meaning startColumn is + a number of byte from the start of the line, not the start of the file)*/ def aux(l : Int, c : Int) : (Int, Int) = { if (l == 0) { val line = sourceCode.offsetToLine(l) @@ -585,38 +615,38 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum iterateParent(pathTerm.symbol) } - def getImportSelectors(parent_path: String, + + /* A known bug with import path is that we are not able to determine the nature of the + imported symbol (or to append several of them if we are importing both a class + and its companionmodule for exemple) */ + def getImportSelectors(parentPath: String, selectors: List[ImportSelector]): Unit = { selectors.foreach(selector => selector match { case SimpleSelector(id) if id.name != "_" => { - addOccurenceId(parent_path, id) + addOccurenceId(parentPath, id) } case RenameSelector(id, _) if id.name != "_" => { - addOccurenceId(parent_path, id) + addOccurenceId(parentPath, id) } case OmitSelector(id) if id.name != "_" => { - addOccurenceId(parent_path, id) + addOccurenceId(parentPath, id) } case _ => }) } - def extractTypeTree(tree: TypeOrBoundsTree) = tree match { - case IsTypeTree(t) => t - } - override def traverseTypeTree(tree: TypeOrBoundsTree)( implicit ctx: Context): Unit = { tree match { case TypeTree.Ident(_) => { - val typetree = extractTypeTree(tree) + val typetree = tree.typetree addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, createRange(typetree.pos)) } case TypeTree.Select(qualifier, _) => { - val typetree = extractTypeTree(tree) + val typetree = tree.typetree val range = rangeSelect(typetree.symbol.trueName, typetree.pos) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, @@ -625,7 +655,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } case TypeTree.Projection(qualifier, x) => { - val typetree = extractTypeTree(tree) + val typetree = tree.typetree val range = rangeSelect(typetree.symbol.trueName, typetree.pos) addOccurenceTypeTree(typetree, s.SymbolOccurrence.Role.REFERENCE, @@ -655,7 +685,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum position */ - val typetree = extractTypeTree(tree) + val typetree = tree.typetree val start = typetree.pos.start val end = typetree.pos.end if (sourceCode.peek(start, end) == typetree.symbol.name) { @@ -686,20 +716,26 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } + + /* Finding the range of init symbols is not intuitive. We can determine it on a classdef. + [fittedInitClassRange] is used to transmit this information to the corresponding symbol */ var fittedInitClassRange: Option[s.Range] = None - var forceAddBecauseParents: Boolean = false - var classStacks : List[Symbol] = Nil - def getNumberParametersInit(defdef: DefDef)(implicit ctx: Context): Int = { - defdef match { - case DefDef(_, typeParams, paramss, _, _) => - paramss.foldLeft(0)((old, c) => old + c.length) + typeParams.length - case _ => 0 + /* At each point of the traversal [classStacks] is the list of classes currently being defined + Ex: + class Foo { + class Bar { + ??? // classStacks = Bar :: Foo :: Nil + } + ??? // classStacks = Foo :: Nil } - } + */ + var classStacks : List[Symbol] = Nil - var disableConstrParamTraversal = false + /* Is the term we are currently seeing the rhs of an assignement? */ + var isAssignedTerm = false + /* Create a mapping from parameter name to parameter position */ def generateParamsPosMapping(cdef: DefDef)(implicit ctx: Context): Map[String, s.Range] = { val DefDef(_, _, params, _, _) = cdef val start = Map[String, s.Range]() @@ -711,14 +747,12 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum ) } - var isAssignedTerm = false - override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Import(path, selectors) => val key = (tree.symbol.trueName, tree.pos.start) - if (!package_definitions(key)) { - package_definitions += key + if (!packageDefinitions(key)) { + packageDefinitions += key getImportSelectors(getImportPath(path), selectors) } case Term.New(ty) => { @@ -728,17 +762,25 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum super.traverseTree(tree) } case ClassDef(classname, constr, parents, derived, selfopt, statements) => { + val offsetSymbolClass = + if(tree.symbol.flags.is(Flags.Object)) -1 + else 0 - - val offsetSymbolClass = - if(tree.symbol.flags.is(Flags.Object)) -1 - else 0 // we first add the class to the symbol list addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, - createRange(tree.symbol.pos.startLine, tree.symbol.pos.startColumn + offsetSymbolClass, tree.symbol.trueName.length)) - // then the constructor - + createRange(tree.symbol.pos.startLine, + tree.symbol.pos.startColumn + offsetSymbolClass, + tree.symbol.trueName.length)) + + /* Before adding the constructor symbol, we must find its position. Two options here: + - we've got no type parameters: `class Foo {...}` -> the symbol is put after `Foo` + - we've got some typeparameters: `class Foo[X] {...}` -> the symbol is put after [X] + In order to find the correct position in the last case, we put ourself on the rightmost bound + of all type parameters, that means before the last ']'. Then, we move one character right to + pass the ']' while making sure to skip whitespaces and comments */ + + /* The position is put in [fittedInitClassRange] to be transmitted to the defdef of */ val DefDef(_, typesParameters, _, _, _) = constr if (typesParameters.isEmpty) { fittedInitClassRange = Some( @@ -750,19 +792,16 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum fittedInitClassRange = Some(createRange(0, end_)) } - disableConstrParamTraversal = true - traverseTree(constr) - disableConstrParamTraversal = false + traverseTree(constr) fittedInitClassRange = None // we add the parents to the symbol list - forceAddBecauseParents = !(tree.symbol.flags.is(Flags.Case)) parents.foreach(_ match { case IsTypeTree(t) => traverseTypeTree(t) case IsTerm(t) => traverseTree(t) }) - forceAddBecauseParents = false + selfopt match { case Some(vdef @ ValDef(name, type_, _)) => { // If name is "_" then it means it is in fact "this". We don't @@ -795,6 +834,15 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum derived.foreach(traverseTypeTree) + /* The last part is to go through every statements. + As usual, we must take care of how we do it as some statements are + accessors for parameters and we don't want to add duplicate information. + If a statement is a parameter accessor we add the corresponding occurence as it + wasn't done when we saw the symbol. + If it's only a parameter (meaning a type parameter) we already added it + before, so we do nothing. + Otherwise we proceed a usual + */ classStacks = tree.symbol :: classStacks val paramsPosMapping = generateParamsPosMapping(constr) @@ -808,8 +856,14 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum traverseTree(statement) } }) + + classStacks = classStacks.tail } + // If we have a symbol with a [fittedInitClassRange] we are sure it is a primary constructor + // We only record symbols correponding to types as symbols for value parameters will be added + // by traversing the class statements. + // Statement should be in this case case DefDef("", typeparams, valparams, type_, statements) if fittedInitClassRange != None => { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, @@ -825,10 +879,12 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum typeparams.foreach(traverseTree) } + // An object should have no init symbols case DefDef("", _, _, _, _) if tree.symbol.owner.flags.is(Flags.Object) => { } case Term.Assign(lhs, rhs) => { + // We make sure to set [isAssignedTerm] to true on the lhs isAssignedTerm = true traverseTree(lhs) isAssignedTerm = false @@ -836,6 +892,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } case IsDefinition(cdef) => { + // For a definition we must deal the special case of protected and private + // definitions if (cdef.symbol.flags.is(Flags.Protected)) { cdef.symbol.protectedWithin match { case Some(within) => { @@ -861,19 +919,21 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum case _ => } } + if (tree.symbol.trueName != "") { val pos = tree.symbol.pos var rangeSymbol = createRange(pos.startLine, pos.startColumn, tree.symbol.trueName.length) + // In dotty definition of auxilliary constructors (ex def this(xxxx)) are represented + // by a DefDef("", ..). This conditions finds such patterns and set a correct rangeSymbol for them. if (tree.symbol.trueName == "" && sourceCode.peek(pos.start, pos.start + 4) == "this") { - rangeSymbol = createRange(pos.startLine, pos.startColumn, pos.startColumn + 4) + rangeSymbol = createRange(pos.startLine, pos.startColumn, 4) } - addOccurenceTree(tree, - s.SymbolOccurrence.Role.DEFINITION, - rangeSymbol) + addOccurenceTree(tree, + s.SymbolOccurrence.Role.DEFINITION, + rangeSymbol) } - //if (!disableConstrParamTraversal) super.traverseTree(cdef) } @@ -889,40 +949,48 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum if (sourceCode.peek(tree.pos.start, tree.pos.end).endsWith("this")) { rangeThis = createRange(tree.pos.startLine, tree.pos.startColumn, tree.symbol.trueName.length) } - /*range = s.Range(tree.pos.startLine, tree.pos.startColumn, - tree.pos.endLine, - )*/ addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, rangeThis) } - case Term.Super(_, Some(id)) => - { - addOccurence(classStacks.head, - s.SymbolOccurrence.Role.DEFINITION, - createRange(id.pos)) - super.traverseTree(tree) - } + case Term.Super(_, Some(id)) => { + addOccurence(classStacks.head, + s.SymbolOccurrence.Role.DEFINITION, + createRange(id.pos)) + super.traverseTree(tree) + } case Term.Select(qualifier, _) => { var range = rangeSelect(tree.symbol.trueName, tree.pos) + /* This branch deals with select of a `this`. Their is two options: + - The select of this is explicit (`C.this`). To know if we are in this case we + check if the end of our position in the sourceCode corresponds to a "this". + - The select is implicit and was compiler generated. We will force to add it if and only if + the qualifier itself was user created + */ var shouldForceAdd = false if (tree.symbol.trueName == "") { if (tree.pos.start == tree.pos.end && sourceCode.peek(tree.pos.start - 5, tree.pos.start - 1) == "this") { - range = createRange(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, 4) + range = createRange(0, tree.pos.start - 5, 4) shouldForceAdd = true } else { - range = createRange(tree.pos.endLine, tree.pos.endColumn, tree.pos.endLine, tree.pos.endColumn) + range = createRange(tree.pos.endLine, tree.pos.endColumn) shouldForceAdd = qualifier.isUserCreated } } + /* We do not forget to disable the [isAssignedTerm] flag when exploring the qualifier + of our select*/ val temp = isAssignedTerm isAssignedTerm = false super.traverseTree(tree) isAssignedTerm = temp - addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable) && !tree.symbol.flags.is(Flags.PrivateLocal)) + + /* If we selected a term x which is a mutable variable without a private local flag we want + to record a call to the function x_= instead. We set the corresponding flag on*/ + val isMutableAssignement = isAssignedTerm && tree.symbol.flags.is(Flags.Mutable) && !tree.symbol.flags.is(Flags.PrivateLocal) + addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isMutableAssignement) } case Term.Ident(name) => { @@ -934,14 +1002,15 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } case Term.Inlined(Some(c), b, d) => { - def extractPos(x: TermOrTypeTree) = x match { - case IsTerm(t) => t.pos - case IsTypeTree(t) => t.pos - } - def extractSymbol(x: TermOrTypeTree) = x match { - case IsTerm(t) => t.symbol - case IsTypeTree(t) => t.symbol - } + /* In theory files should be compiled with -Yno-inline before running semanticdb. + If this is not the case, here is a fallback to heuristically determine which predefFunction + corresponds to an inlined term. + + We peek the character below the inline node. + If it is an "l" (for locally), then we have a locally predef call + If it is an "i" (for implicitly") then it is an implicitly call + If it is an "a" it is an assert + */ def getPredefFunction(pos: Int): String = { sourceCode.peek(pos, pos+1) match { case "l" => "locally" @@ -949,9 +1018,9 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum case "a" => "assert" } } - val parentSymbol = iterateParent(extractSymbol(c)) + val parentSymbol = iterateParent(c.symbol) if (parentSymbol == "dotty/DottyPredef.") { - val pos = extractPos(c) + val pos = c.pos val function = getPredefFunction(pos.start) val range = createRange(pos.startLine, pos.startColumn, pos.startLine, function.length) addOccurencePredef(parentSymbol, function, range) @@ -962,11 +1031,11 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum case PackageClause(_) => val key = (tree.symbol.trueName, tree.pos.start) - if (!package_definitions(key)) { + if (!packageDefinitions(key)) { addOccurenceTree(tree, s.SymbolOccurrence.Role.DEFINITION, createRange(tree.pos.startLine, tree.pos.startColumn + "package ".length, tree.symbol.trueName.length)) - package_definitions += key + packageDefinitions += key } super.traverseTree(tree) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index f44d27ce7a09..3b16d0f53904 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -16,87 +16,107 @@ import java.lang.reflect.InvocationTargetException import dotty.semanticdb.Scala._ import dotty.tools.dotc.util.SourceFile - +import scala.math._ class Tests { - def min(a: Int, b: Int) : Int = if (a > b) b else a - def max(a: Int, b: Int) : Int = if (a > b) a else b - def abs(a: Int, b: Int) : Int = max(a, b) - min(a, b) - def distance(r1 : s.Range, sourceFile : SourceFile)(r2 : s.Range) : Int = { + def distance(r1: s.Range, sourceFile: SourceFile)(r2: s.Range): Int = { val s1 = sourceFile.lineToOffset(max(r1.startLine, 0)) + r1.startCharacter val s2 = sourceFile.lineToOffset(max(r2.startLine, 0)) + r2.startCharacter val e1 = sourceFile.lineToOffset(max(r1.endLine, 0)) + r1.endCharacter val e2 = sourceFile.lineToOffset(max(r2.endLine, 0)) + r2.endCharacter - max(abs(s1, s2), abs(e1, e2)) + max(abs(s1 - s2), abs(e1 - e2)) } - def compareOccurrences(tastyOccurrences : Seq[s.SymbolOccurrence], - scalaOccurrences : Seq[s.SymbolOccurrence], - sourceCode : String) - : Boolean= { - val sourceFile = SourceFile.virtual("", sourceCode) - val symbols = tastyOccurrences.groupBy(_.symbol) - val localTastyToScala = HashMap[String, String]() - val localScalaToTasty = HashMap[String, String]() - val translator = HashMap[(s.Range, String), s.SymbolOccurrence]() - - // from is in tasty space, to in scala space - def checkIfTranslatableSymbol(from : String, to : String) : Boolean = { - if (from.isLocal != to.isLocal) { - false - } else { - if (from.isLocal) { - if(localTastyToScala.getOrElse(from, to) == to && + /* This is a most powerfull way to compare two lists of occurences + than just to print the diff. The diff will fail local symbols have not + the same numer (even if their is no conflicts) and will also fail for symbols + with small differences. + The goal here is to have a method returning true if the tasty occurences and + the scala occurences are the "same" (no local conflicts, positions can be slightly + different). */ + def compareOccurrences(tastyOccurrences: Seq[s.SymbolOccurrence], + scalaOccurrences: Seq[s.SymbolOccurrence], + sourceCode: String): Boolean = { + val sourceFile = SourceFile.virtual("", sourceCode) + val symbols = tastyOccurrences.groupBy(_.symbol) + val localTastyToScala = HashMap[String, String]() + val localScalaToTasty = HashMap[String, String]() + val translator = HashMap[(s.Range, String), s.SymbolOccurrence]() + + /* Check if we can translate the symbol [from] to the symbol [to] + True if their is no local clash (ex if we know local2 -> local3 then local2 clash with local4) + [from] is in tasty space, [to] in scala space + */ + def checkIfTranslatableSymbol(from: String, to: String): Boolean = { + if (from.isLocal != to.isLocal) { + false + } else { + if (from.isLocal) { + if (localTastyToScala.getOrElse(from, to) == to && localScalaToTasty.getOrElse(to, from) == from) { localTastyToScala += (from -> to) localScalaToTasty += (to -> from) - true - } else { - false - } - } else { true + } else { + false } + } else { + true } } + } - if (tastyOccurrences.length != scalaOccurrences.length) { - false - } else { - scalaOccurrences.forall(occurrence => { - if (occurrence.symbol.isLocal || - symbols.contains(localScalaToTasty.getOrElse(occurrence.symbol, occurrence.symbol))) { - val siblings = - if (occurrence.symbol.isLocal) tastyOccurrences - else symbols(occurrence.symbol) - - val nearest = siblings.minBy(c => distance(occurrence.range.get, sourceFile)(c.range.get)) - - if (!checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol) || - translator.contains((nearest.range.get, nearest.symbol)) || - distance(occurrence.range.get, sourceFile)(nearest.range.get) > 5) { - println(checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol)) - false - } else { - if (!occurrence.symbol.isLocal) - translator += ((nearest.range.get, nearest.symbol) -> occurrence) - true - } - } else { + /* If we do not have the same number of occurrences in both lists we know we failed + the test */ + if (tastyOccurrences.length != scalaOccurrences.length) { + false + } else { + /* We associate every scala occurrence to the nearest tasty occurence with the + same name (if it is a global symbol) or local symbol */ + scalaOccurrences.forall(occurrence => { + if (occurrence.symbol.isLocal || + symbols.contains(localScalaToTasty.getOrElse(occurrence.symbol, + occurrence.symbol))) { + val siblings = + if (occurrence.symbol.isLocal) tastyOccurrences + else symbols(occurrence.symbol) + + val nearest = siblings.minBy(c => + distance(occurrence.range.get, sourceFile)(c.range.get)) + + /* If these two symbols can be translated, meaning: + - [nearest] was not associated with any other scala occurrence + - if [occurrence] is a local symbol it does not clash with [nearest] + - they are "near" in the source code */ + if (!checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol) || + translator.contains((nearest.range.get, nearest.symbol)) || + distance(occurrence.range.get, sourceFile)(nearest.range.get) > 5) { + println( + checkIfTranslatableSymbol(nearest.symbol, occurrence.symbol)) false + } else { + if (!occurrence.symbol.isLocal) + translator += ((nearest.range.get, nearest.symbol) -> occurrence) + true } - }) - } + } else { + false + } + }) + } } final def tastyClassDirectory = { val root = "out/bootstrap/dotty-semanticdb/" val files = Paths.get(root).toFile().listFiles val scalaFolderReg = """scala-(\d+)\.(\d+)""".r - val (_, _, path) = files.collect(file => file.getName match { - case scalaFolderReg(major, minor) => (major, minor, file.getName) - }).max + val (_, _, path) = files + .collect(file => + file.getName match { + case scalaFolderReg(major, minor) => (major, minor, file.getName) + }) + .max Paths.get(root, path, "test-classes") } @@ -114,7 +134,7 @@ class Tests { final def allTastyFiles = Utils.getTastyFiles(tastyClassDirectory, "example") /** Returns the SemanticDB for this Scala source file. */ - def getTastySemanticdb(classPath: Path, scalaFile: Path) : s.TextDocument = { + def getTastySemanticdb(classPath: Path, scalaFile: Path): s.TextDocument = { val classNames = Utils.getClassNamesCached(scalaFile, allTastyFiles) val sdbconsumer = new SemanticdbConsumer(scalaFile) @@ -175,7 +195,8 @@ class Tests { @Test def testLocals(): Unit = checkFile("example/Locals.scala") //deactivated @Test def testMacroAnnotations(): Unit = checkFile("example/MacroAnnotations.scala") @Test def testMethods(): Unit = checkFile("example/Methods.scala") - @Test def testMultiArguments(): Unit = checkFile("example/MultiArguments.scala") + @Test def testMultiArguments(): Unit = + checkFile("example/MultiArguments.scala") @Test def testObjects(): Unit = checkFile("example/Objects.scala") @Test def testOverrides(): Unit = checkFile("example/Overrides.scala") @Test def testPrefixes(): Unit = checkFile("example/Prefixes.scala") @@ -183,9 +204,11 @@ class Tests { @Test def testSelfUse(): Unit = checkFile("example/SelfUse.scala") @Test def testTraits(): Unit = checkFile("example/Traits.scala") @Test def testTypes(): Unit = checkFile("example/Types.scala") - @Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols + @Test def testTypesAnnotations(): Unit = + checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols @Test def testVals(): Unit = checkFile("example/Vals.scala") - @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") + @Test def testDependantModule(): Unit = + checkFile("example/DependantModule.scala") @Test def testNew(): Unit = checkFile("example/New.scala") @Test def testIgnoredSymbol(): Unit = checkFile("example/IgnoredSymbol.scala") @Test def testCase(): Unit = checkFile("example/Case.scala") @@ -197,4 +220,4 @@ class Tests { @Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala") @Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala") -} \ No newline at end of file +} From df71b4afaa5bea4674d24f2207e85d540feee612 Mon Sep 17 00:00:00 2001 From: poechsel Date: Tue, 22 Jan 2019 14:47:46 +0100 Subject: [PATCH 37/42] Implement nested multiline comments for pseudo parsing --- .../src/main/scala/example/Classes.scala | 3 ++- .../src/dotty/semanticdb/SourceFile.scala | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/semanticdb/input/src/main/scala/example/Classes.scala b/semanticdb/input/src/main/scala/example/Classes.scala index d5c0d3f3970b..1d0770e805f3 100644 --- a/semanticdb/input/src/main/scala/example/Classes.scala +++ b/semanticdb/input/src/main/scala/example/Classes.scala @@ -1,7 +1,8 @@ package example class CDep[X] -class CDependenat[X](var x1: CDep[X]) +class CDependenat[X /* This is a comment /* and a nested comment +*/*/](var x1: CDep[X]) class CVarArg(var x1 : Int) diff --git a/semanticdb/src/dotty/semanticdb/SourceFile.scala b/semanticdb/src/dotty/semanticdb/SourceFile.scala index 15aa2a691bf4..c53579b6e338 100644 --- a/semanticdb/src/dotty/semanticdb/SourceFile.scala +++ b/semanticdb/src/dotty/semanticdb/SourceFile.scala @@ -31,14 +31,28 @@ class SourceFile(path: java.nio.file.Path) { def nextCharacterSkipComments(start : Int): Int = { def aux(start : Int) : Int = { - var i = start + var i = start if (i+2 <= sourceCode.length && sourceCode.substring(i, i+2) == "//") { while (i < sourceCode.length && sourceCode(i) != '\n') - i += 1 + i += 1 return i+1 } else if (i+2 <= sourceCode.length && sourceCode.substring(i, i+2) == "/*") { - while (i + 2 <= sourceCode.length && sourceCode.substring(i, i+2) != "*/") + var nestedCount = 0 + i += 2 + while (i + 2 <= sourceCode.length && + !(sourceCode.substring(i, i+2) == "*/" && + nestedCount == 0)) { + val s = sourceCode.substring(i, i+2) + if (s == "/*") { + nestedCount += 1 + i += 1 + } + if (s == "*/") { + nestedCount -= 1 + i += 1 + } i += 1 + } return i+2 } else { while (i < sourceCode.length && sourceCode(i).isWhitespace) From d9e52700a991482e38cfa8d84d9eda7424870f69 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 23 Jan 2019 12:54:44 +0100 Subject: [PATCH 38/42] Add documentation about new flags --- .../src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala | 1 - library/src/scala/tasty/reflect/FlagsOps.scala | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala index b16094e9d649..cb65129d3d25 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala @@ -43,7 +43,6 @@ trait FlagsOpsImpl extends scala.tasty.reflect.FlagsOps with CoreImpl { def StableRealizable: Flags = core.Flags.StableRealizable def Param: Flags = core.Flags.Param def ParamAccessor: Flags = core.Flags.ParamAccessor - def JavaDefined: Flags = core.Flags.JavaDefined def Enum: Flags = core.Flags.Enum def ModuleClass: Flags = core.Flags.ModuleClass def PrivateLocal: Flags = core.Flags.PrivateLocal diff --git a/library/src/scala/tasty/reflect/FlagsOps.scala b/library/src/scala/tasty/reflect/FlagsOps.scala index 0f87394fbed6..6eff5e459a4f 100644 --- a/library/src/scala/tasty/reflect/FlagsOps.scala +++ b/library/src/scala/tasty/reflect/FlagsOps.scala @@ -102,16 +102,19 @@ trait FlagsOps extends Core { /** Is this symbol a parameter accessor */ def ParamAccessor: Flags - def JavaDefined: Flags - + /** Is this symbol an enum */ def Enum: Flags + /** Is this symbol a module class */ def ModuleClass: Flags + /** Is this symbol labeled private[this] */ def PrivateLocal: Flags + /** Is this symbol a package */ def Package: Flags + /** Is this symbol an implementation class of a Scala2 trait */ def ImplClass: Flags } From 055e732c1b8a25bde7f5b297f69b9448980333f7 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 23 Jan 2019 17:48:19 +0100 Subject: [PATCH 39/42] add new flags to showFlags --- library/src/scala/tasty/reflect/Printers.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 000eff2a8ddc..0432c4d83056 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -129,6 +129,11 @@ trait Printers if (flags.is(Flags.StableRealizable)) flagList += "Flags.StableRealizable" if (flags.is(Flags.Param)) flagList += "Flags.Param" if (flags.is(Flags.ParamAccessor)) flagList += "Flags.ParamAccessor" + if (flags.is(Flags.Enum)) flagList += "Flags.Enum" + if (flags.is(Flags.ModuleClass)) flagList += "Flags.ModuleClass" + if (flags.is(Flags.PrivateLocal)) flagList += "Flags.PrivateLocal" + if (flags.is(Flags.Package)) flagList += "Flags.Package" + if (flags.is(Flags.ImplClass)) flagList += "Flags.ImplClass" flagList.result().mkString(" | ") } @@ -504,6 +509,11 @@ trait Printers if (flags.is(Flags.StableRealizable)) flagList += "stableRealizable" if (flags.is(Flags.Param)) flagList += "param" if (flags.is(Flags.ParamAccessor)) flagList += "paramAccessor" + if (flags.is(Flags.Enum)) flagList += "enum" + if (flags.is(Flags.ModuleClass)) flagList += "moduleClass" + if (flags.is(Flags.PrivateLocal)) flagList += "private[this]" + if (flags.is(Flags.Package)) flagList += "package" + if (flags.is(Flags.ImplClass)) flagList += "implClass" flagList.result().mkString("/*", " ", "*/") } From 202917365dc5dc59e64a0033b8d9762711fd2dae Mon Sep 17 00:00:00 2001 From: poechsel Date: Thu, 24 Jan 2019 17:31:15 +0100 Subject: [PATCH 40/42] Fix bug with java class not recorded as types --- .../dotty/semanticdb/SemanticdbConsumer.scala | 78 +++++++++---------- semanticdb/test/dotty/semanticdb/Tests.scala | 2 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index ebc5f24b7d4e..fbc9fee63792 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -119,6 +119,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } implicit class SymbolExtender(symbol: Symbol) { + def exists = !(symbol.name == "" || symbol == NoSymbol) /* Return true if symbol represents the definition of a var setter, false otherwise. We return true if the extract of source code corresponding to the position of the symbol is the same as the symbol name. Ex: @@ -192,15 +193,15 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def isValueParameter: Boolean = symbol.isParameter && !symbol.isType && !symbol.flags.is(Flags.ParamAccessor) - def isJavaClass: Boolean = symbol.isClass && symbol.flags.is(Flags.JavaDefined) + def isJavaClass: Boolean = (symbol.isClass || symbol.isObject) && symbol.flags.is(Flags.JavaDefined) def isSelfParameter(implicit ctx: Context): Boolean = - symbol != NoSymbol && symbol.owner == symbol + symbol.exists && symbol.owner == symbol def isSemanticdbLocal(implicit ctx: Context): Boolean = { def definitelyGlobal = symbol.isPackage def definitelyLocal = - symbol == NoSymbol || + !symbol.exists || (symbol.owner.isTerm && !symbol.isParameter) || ((symbol.owner.isAliasType || symbol.owner.isAbstractType) && !symbol.isParameter) || symbol.isSelfParameter || @@ -217,7 +218,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum symbol.name == "" def isSyntheticConstructor(implicit ctx: Context): Boolean = { - val isObjectConstructor = symbol.isConstructor && symbol.owner != NoSymbol && symbol.owner.flags.is(Flags.Object) + val isObjectConstructor = symbol.isConstructor && symbol.owner.exists && symbol.owner.flags.is(Flags.Object) val isModuleConstructor = symbol.isConstructor && symbol.owner.isClass val isTraitConstructor = symbol.isConstructor && symbol.owner.isTrait val isInterfaceConstructor = symbol.isConstructor && symbol.owner.flags.is(Flags.JavaDefined) && symbol.owner.isTrait @@ -268,7 +269,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum (/*isFieldForPrivateThis ||*/ isFieldForOther) && !isJavaDefined } def isUselessField(implicit ctx: Context): Boolean = { - symbol.isScalacField && symbol.owner != NoSymbol + symbol.isScalacField && symbol.owner.exists } def isUsefulField(implicit ctx: Context): Boolean = { symbol.isScalacField && !symbol.isUselessField @@ -277,7 +278,11 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum symbol.flags.is(Flags.CaseAcessor) && symbol.trueName.contains("$") } def isSyntheticJavaModule(implicit ctx: Context): Boolean = { - !symbol.flags.is(Flags.Package) && symbol.flags.is(Flags.JavaDefined) && symbol.flags.is(Flags.Object) + val resolved = symbol match { + case IsClassSymbol(c) => resolveClass(c) + case _ => symbol + } + !resolved.flags.is(Flags.Package) && resolved.flags.is(Flags.JavaDefined) && resolved.flags.is(Flags.Object) } def isAnonymousClassConstructor(implicit ctx: Context): Boolean = { symbol.isConstructor && symbol.owner.isAnonymousClass @@ -299,7 +304,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } def isStaticMember(implicit ctx: Context): Boolean = - (symbol == NoSymbol) && + symbol.exists && (symbol.flags.is(Flags.Static) || symbol.owner.flags.is(Flags.ImplClass) || /*symbol.annots.find(_ == ctx.definitions.ScalaStaticAnnot)*/ false) @@ -308,8 +313,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def isInitChild(implicit ctx: Context): Boolean = { - if (!(symbol.name == "" || symbol == NoSymbol) - && symbol.owner != NoSymbol) { + if (symbol.exists && symbol.owner.exists) { return symbol.owner.name == "" || symbol.owner.isInitChild } else { return false @@ -322,13 +326,13 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def isAnonymousInit(implicit ctx: Context): Boolean = { - return symbol.owner != NoSymbol && + return symbol.exists && symbol.owner.exists && (symbol.owner.isAnonymousFunction || symbol.owner.isAnonymousClass) && symbol.name == "" } def isUseless(implicit ctx: Context): Boolean = { - (symbol.name == "" || symbol == NoSymbol) || + !symbol.exists || symbol.isReservedName || symbol.isAnonymousInit || symbol.isDefaultGetter || @@ -343,9 +347,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum symbol.isSyntheticCaseAccessor || symbol.isRefinementClass || symbol.isSyntheticJavaModule - // isSyntheticJavaModule disable the symbol Class in - // Class.forName(???) to be recorded as Class is considered to - // be a class in dotty, not a typed. } def isUseful(implicit ctx: Context): Boolean = !symbol.isUseless def isUselessOccurrence(implicit ctx: Context): Boolean = { @@ -392,42 +393,41 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } def iterateParent(symbol: Symbol, isMutableAssignement:Boolean=false): String = { - if (symbol.name == "" || symbol.name == "") then { + if (!symbol.exists || symbol.name == "") then { "" } else { + val rsymbol = symbol match { + case IsClassSymbol(c) => resolveClass(c) + case _ => symbol + } val previous_symbol = /* When we consider snipper of the form: `abstract class DepAdvD[CC[X[C] <: B], X[Z], C] extends DepTemp`, The symbol for C will be something like example/DepAdvD#``().[CC].[X].[C]. This is illogic: a init method can't have any child. Thus, when the current symbol is a typeparameter, and the owner is an init, we can just "jump" over the init. */ - if (symbol.owner.name == "" && symbol.isType) - iterateParent(symbol.owner.owner) + if (rsymbol.owner.name == "" && rsymbol.isType) + iterateParent(rsymbol.owner.owner) else - iterateParent(symbol.owner) + iterateParent(rsymbol.owner) - val isdef = symbol match {case IsDefSymbol(_) => true case _ => false} - val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName + val isdef = rsymbol match {case IsDefSymbol(_) => true case _ => false} + val symbolName = if (isMutableAssignement) rsymbol.trueName + "_=" else rsymbol.trueName val next_atom = - if (symbol.isPackage) { + if (rsymbol.isPackage) { d.Package(symbolName) - } else if (symbol.isObject) { - symbol match { - case IsClassSymbol(classsymbol) => - d.Term(resolveClass(classsymbol).trueName) - case _ => - d.Term(symbolName) - } - } else if (symbol.isValMethod && !symbol.isVarAccessor) { + } else if (rsymbol.isObject && !rsymbol.isJavaClass) { + d.Term(symbolName) + } else if (rsymbol.isValMethod && !rsymbol.isVarAccessor) { d.Term(symbolName) - } else if (symbol.isMethod || symbol.isUsefulField || symbol.isVarAccessor) { + } else if (rsymbol.isMethod || rsymbol.isUsefulField || rsymbol.isVarAccessor) { d.Method(symbolName, - disimbiguate(previous_symbol + symbolName, symbol)) - } else if (symbol.isTypeParameter) { + disimbiguate(previous_symbol + symbolName, rsymbol)) + } else if (rsymbol.isTypeParameter) { d.TypeParameter(symbolName) - } else if (symbol.isValueParameter) { + } else if (rsymbol.isValueParameter) { d.Parameter(symbolName) - } else if (symbol.isType || symbol.isTrait) { + } else if (rsymbol.isType || rsymbol.isJavaClass) { d.Type(symbolName) } else { d.Term(symbolName) @@ -451,7 +451,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum typeSymbol: s.SymbolOccurrence.Role, range: s.Range, isMutableAssignement:Boolean = false): Unit = { - if (symbol.name == "") return + if (!symbol.exists) return val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName val (symbolPath, isGlobal) = @@ -515,7 +515,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum range: s.Range, forceAdd: Boolean = false, isMutableAssignement: Boolean = false): Unit = { - if (tree.symbol.isUseful && + if (!tree.symbol.isUselessOccurrence && tree.symbol.isMutableSetterExplicit(typeSymbol) && (tree.isUserCreated || forceAdd)) { addOccurence(tree.symbol, typeSymbol, range, isMutableAssignement) @@ -525,7 +525,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def addOccurenceTypeTree(typetree: TypeTree, typeSymbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - if (typetree.symbol.isUseful && typetree.isUserCreated) { + if (!typetree.symbol.isUselessOccurrence && typetree.isUserCreated) { addOccurence(typetree.symbol, typeSymbol, range) } } @@ -533,7 +533,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def addOccurencePatternTree(tree: Pattern, typeSymbol: s.SymbolOccurrence.Role, range: s.Range): Unit = { - if (tree.symbol.isUseful && tree.isUserCreated) { + if (!tree.symbol.isUselessOccurrence && tree.isUserCreated) { addOccurence(tree.symbol, typeSymbol, range) } } @@ -920,7 +920,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } - if (tree.symbol.trueName != "") { + if (tree.symbol.exists) { val pos = tree.symbol.pos var rangeSymbol = createRange(pos.startLine, pos.startColumn, tree.symbol.trueName.length) diff --git a/semanticdb/test/dotty/semanticdb/Tests.scala b/semanticdb/test/dotty/semanticdb/Tests.scala index 3b16d0f53904..480f38f5ea69 100644 --- a/semanticdb/test/dotty/semanticdb/Tests.scala +++ b/semanticdb/test/dotty/semanticdb/Tests.scala @@ -205,7 +205,7 @@ class Tests { @Test def testTraits(): Unit = checkFile("example/Traits.scala") @Test def testTypes(): Unit = checkFile("example/Types.scala") @Test def testTypesAnnotations(): Unit = - checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols + checkFile("example/TypesAnnotations.scala") @Test def testVals(): Unit = checkFile("example/Vals.scala") @Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala") From 9f708d8694dde1ba5c65aa0d616135a57b4085b8 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 30 Jan 2019 19:41:10 +0100 Subject: [PATCH 41/42] Add comment to distinguish between scala(s semanticdb imported methods and our methods + fix PR comments --- .../tastyreflect/TypeOrBoundsOpsImpl.scala | 1 - .../scala/tasty/reflect/TypeOrBoundsOps.scala | 1 - semanticdb/src/dotty/semanticdb/Main.scala | 7 +- .../dotty/semanticdb/SemanticdbConsumer.scala | 75 ++++++++++--------- vscode-dotty/package-lock.json | 4 +- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index 52b103f3224b..f375733f5e00 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -23,7 +23,6 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI if (tpe.classSymbol.exists) Some(tpe.classSymbol.asClass) else None def typeSymbol(implicit ctx: Context): Symbol = tpe.typeSymbol - //def pos(implicit ctx: Context): Position = tpe.pos } def ConstantTypeDeco(x: ConstantType): Type.ConstantTypeAPI = new Type.ConstantTypeAPI { diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 41937d787ce7..1063d4345e25 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -55,7 +55,6 @@ trait TypeOrBoundsOps extends Core { def widen(implicit ctx: Context): Type def classSymbol(implicit ctx: Context): Option[ClassSymbol] def typeSymbol(implicit ctx: Context): Symbol - //def pos(implicit ctx: Context): Position } val IsType: IsTypeModule diff --git a/semanticdb/src/dotty/semanticdb/Main.scala b/semanticdb/src/dotty/semanticdb/Main.scala index 0d6a55329170..f9e444573dc2 100644 --- a/semanticdb/src/dotty/semanticdb/Main.scala +++ b/semanticdb/src/dotty/semanticdb/Main.scala @@ -45,14 +45,14 @@ object Main { var cleanedArgs = args cleanedArgs += "out" -> cleanedArgs.getOrElse("out", "out.semanticdb") if (cleanedArgs.contains("help") || !cleanedArgs.contains("input")) { - return None + None } else { cleanedArgs += "classpath" -> cleanedArgs.getOrElse("classpath", Files.createTempDirectory("semanticdb").toString) val tempFolder = new File(cleanedArgs("classpath")); if (!tempFolder.exists()){ tempFolder.mkdir(); } - return Some(cleanedArgs) + Some(cleanedArgs) } } case None => None @@ -69,8 +69,7 @@ object Main { cliArgs("input") :: Nil - val reporter = driver.process(compilerParams.toArray) - return reporter + driver.process(compilerParams.toArray) } diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index fbc9fee63792..441040be093b 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -191,6 +191,45 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def isTrait: Boolean = symbol.flags.is(Flags.Trait) + def isConstructor(implicit ctx: Context): Boolean = + symbol.name == "" + + def isVarAccessor(implicit ctx: Context): Boolean = { + symbol.isVal && symbol.flags.is(Flags.Mutable) + } + + def isValMethod(implicit ctx: Context): Boolean = { + symbol.isMethod && { + (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.Stable) ) || + (symbol.isUsefulField && !symbol.flags.is(Flags.Mutable) ) + } + } + + def isAnonymousClassConstructor(implicit ctx: Context): Boolean = { + symbol.isConstructor && symbol.owner.isAnonymousClass + } + + def isAnonymousSelfParameter(implicit ctx: Context): Boolean = { + symbol.isSelfParameter && { + symbol.name == tpnme.this_.toString || // hardlinked in ClassSignature.self + symbol.name.startsWith("x$") // wildcards can't be referenced: class A { _: B => } + } + } + + def isWildCard(implicit ctx: Context): Boolean = { + symbol.name.startsWith(tpnme.WILDCARD.toString) && + symbol.name != tpnme.THIS.toString + } + + def isAnonymousInit(implicit ctx: Context): Boolean = { + return symbol.exists && symbol.owner.exists && + (symbol.owner.isAnonymousFunction || symbol.owner.isAnonymousClass) && + symbol.name == "" + } + + /* The following methods are directly extracted from the scala + implementation of SemanticDB (scalameta/semanticdb/scalac/library/src/main/scala/scala/meta/internal/semanticdb/scalac/SymbolOps.scala) + */ def isValueParameter: Boolean = symbol.isParameter && !symbol.isType && !symbol.flags.is(Flags.ParamAccessor) def isJavaClass: Boolean = (symbol.isClass || symbol.isObject) && symbol.flags.is(Flags.JavaDefined) @@ -214,9 +253,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum !definitelyGlobal && (definitelyLocal || ownerLocal) } - def isConstructor(implicit ctx: Context): Boolean = - symbol.name == "" - def isSyntheticConstructor(implicit ctx: Context): Boolean = { val isObjectConstructor = symbol.isConstructor && symbol.owner.exists && symbol.owner.flags.is(Flags.Object) val isModuleConstructor = symbol.isConstructor && symbol.owner.isClass @@ -247,17 +283,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } - def isVarAccessor(implicit ctx: Context): Boolean = { - symbol.isVal && symbol.flags.is(Flags.Mutable) - } - - def isValMethod(implicit ctx: Context): Boolean = { - symbol.isMethod && { - (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.Stable) ) || - (symbol.isUsefulField && !symbol.flags.is(Flags.Mutable) ) - } - } - /* the `isFieldForPrivateThis` is commented out otherwise class members of the form "private[this] val foo" are not converted to symbol occurences. In the original semanticdb this line is commented out. @@ -284,9 +309,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } !resolved.flags.is(Flags.Package) && resolved.flags.is(Flags.JavaDefined) && resolved.flags.is(Flags.Object) } - def isAnonymousClassConstructor(implicit ctx: Context): Boolean = { - symbol.isConstructor && symbol.owner.isAnonymousClass - } def isSyntheticAbstractType(implicit ctx: Context): Boolean = { symbol.flags.is(Flags.Synthetic) && symbol.isAbstractType // these are hardlinked to TypeOps } @@ -297,12 +319,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum symbol.name.startsWith("x$") && symbol.owner.isAnonymousFunction } - def isAnonymousSelfParameter(implicit ctx: Context): Boolean = { - symbol.isSelfParameter && { - symbol.name == tpnme.this_.toString || // hardlinked in ClassSignature.self - symbol.name.startsWith("x$") // wildcards can't be referenced: class A { _: B => } - } - } def isStaticMember(implicit ctx: Context): Boolean = symbol.exists && (symbol.flags.is(Flags.Static) || symbol.owner.flags.is(Flags.ImplClass) || @@ -312,6 +328,8 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum (symbol.isStaticMember && symbol.isClassConstructor) || (symbol.name == tpnme.STATIC_CONSTRUCTOR.toString) } + /* End of methods imported from the scala version of SemanticDB */ + def isInitChild(implicit ctx: Context): Boolean = { if (symbol.exists && symbol.owner.exists) { return symbol.owner.name == "" || symbol.owner.isInitChild @@ -320,17 +338,6 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum } } - def isWildCard(implicit ctx: Context): Boolean = { - symbol.name.startsWith(tpnme.WILDCARD.toString) && - symbol.name != tpnme.THIS.toString - } - - def isAnonymousInit(implicit ctx: Context): Boolean = { - return symbol.exists && symbol.owner.exists && - (symbol.owner.isAnonymousFunction || symbol.owner.isAnonymousClass) && - symbol.name == "" - } - def isUseless(implicit ctx: Context): Boolean = { !symbol.exists || symbol.isReservedName || diff --git a/vscode-dotty/package-lock.json b/vscode-dotty/package-lock.json index e9e6ca8c5b82..65f388b9ef52 100644 --- a/vscode-dotty/package-lock.json +++ b/vscode-dotty/package-lock.json @@ -27,7 +27,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, @@ -296,7 +296,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "requires": { "readable-stream": "^2.3.5", From 4abf222a936d4a7d5c64c6fe8a3e58e92e196dd2 Mon Sep 17 00:00:00 2001 From: poechsel Date: Wed, 30 Jan 2019 20:04:36 +0100 Subject: [PATCH 42/42] Replace Flags.Stable by Flags.StableRealizable in semanticdbconsumer --- semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala index 441040be093b..bfdac6b40c8a 100644 --- a/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala +++ b/semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala @@ -200,7 +200,7 @@ class SemanticdbConsumer(sourceFilePath: java.nio.file.Path) extends TastyConsum def isValMethod(implicit ctx: Context): Boolean = { symbol.isMethod && { - (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.Stable) ) || + (symbol.flags.is(Flags.FieldAccessor) && symbol.flags.is(Flags.StableRealizable) ) || (symbol.isUsefulField && !symbol.flags.is(Flags.Mutable) ) } }