Skip to content

Commit 7f3f5be

Browse files
committed
improve testing infrastructure and fix small bugs
1 parent ad9e2b0 commit 7f3f5be

File tree

3 files changed

+103
-55
lines changed

3 files changed

+103
-55
lines changed

semanticdb/input/src/main/scala/example/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,4 @@ object TypTest {
117117
final val javaEnum = java.nio.file.LinkOption.NOFOLLOW_LINKS
118118
final val clazzOf = classOf[Option[Int]]
119119
}
120-
}
120+
}

semanticdb/src/dotty/semanticdb/SemanticdbConsumer.scala

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
281281
}
282282

283283
def isUseless(implicit ctx: Context): Boolean = {
284-
symbol == NoSymbol ||
284+
(symbol.name == "<none>" || symbol == NoSymbol) ||
285285
symbol.isReservedName ||
286286
symbol.isAnonymousInit ||
287287
symbol.isDefaultGetter ||
@@ -296,6 +296,9 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
296296
symbol.isSyntheticCaseAccessor ||
297297
symbol.isRefinementClass ||
298298
symbol.isSyntheticJavaModule
299+
// isSyntheticJavaModule disable the symbol Class in
300+
// Class.forName(???) to be recorded as Class is considered to
301+
// be a class in dotty, not a typed.
299302
}
300303
def isUseful(implicit ctx: Context): Boolean = !symbol.isUseless
301304
def isUselessOccurrence(implicit ctx: Context): Boolean = {
@@ -427,8 +430,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
427430
isMutableAssignement:Boolean = false): Unit = {
428431
if (symbol.name == "<none>") return
429432

430-
431-
println("===> ", symbol, symbol.flags)
432433
val symbolName = if (isMutableAssignement) symbol.trueName + "_=" else symbol.trueName
433434
val (symbol_path, is_global) = posToRange(symbol.pos) match {
434435
case Some(keyRange)
@@ -456,12 +457,8 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
456457
// dotty will generate a ValDef for the x, but the x will also
457458
// be present in the constructor, thus making a double definition
458459
if (symbolPathsMap.contains(key)) return
459-
//if (is_global) {
460-
symbolPathsMap += key
461-
//}
462-
println(symbol_path,
463-
range,
464-
symbol.flags)
460+
461+
symbolPathsMap += key
465462
occurrences =
466463
occurrences :+
467464
s.SymbolOccurrence(
@@ -490,15 +487,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
490487

491488
val reservedFunctions: List[String] = Nil
492489
def addOccurenceTree(tree: Tree,
493-
type_symbol: s.SymbolOccurrence.Role,
490+
typeSymbol: s.SymbolOccurrence.Role,
494491
range: s.Range,
495492
force_add: Boolean = false,
496493
isMutableAssignement: Boolean = false): Unit = {
497494
if (tree.symbol.isUseful &&
498-
isMutableSetterExplicit(tree.symbol, type_symbol) &&
495+
isMutableSetterExplicit(tree.symbol, typeSymbol) &&
499496
(tree.isUserCreated ||
500-
(force_add && !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#`<init>`().")))) {
501-
addOccurence(tree.symbol, type_symbol, range, isMutableAssignement)
497+
(force_add /*&& !(!tree.isUserCreated && iterateParent(tree.symbol) == "java/lang/Object#`<init>`().")*/))) {
498+
addOccurence(tree.symbol, typeSymbol, range, isMutableAssignement)
502499
}
503500
}
504501
def addOccurenceTypeTree(typetree: TypeTree,
@@ -761,7 +758,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
761758
val start = Map[String, s.Range]()
762759
params.foldLeft(start)((old, statements) => {
763760
statements.foldLeft(old)((old, cval) => {
764-
println(cval)
765761
old + (cval.name -> range(cval, cval.symbol.pos, cval.symbol.trueName))
766762
})
767763
}
@@ -833,9 +829,7 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
833829
forceAddBecauseParents = !(tree.symbol.flags.is(Flags.Case))
834830
parents.foreach(_ match {
835831
case IsTypeTree(t) => traverseTypeTree(t)
836-
case IsTerm(t) => {
837-
traverseTree(t)
838-
}
832+
case IsTerm(t) => traverseTree(t)
839833
})
840834
forceAddBecauseParents = false
841835
selfopt match {
@@ -870,31 +864,20 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
870864
}
871865
case _ =>
872866
}
873-
if (!tree.symbol.flags.is(Flags.Case)) {
867+
874868
classStacks = tree.symbol :: classStacks
875869

876-
println(statements)
877-
println("")
878870
val paramsPosMapping = generateParamsPosMapping(constr)
879-
println(paramsPosMapping)
880-
println("")
881871

882872
statements.foreach(statement => {
883873
if (statement.symbol.flags.is(Flags.ParamAccessor)) {
884874
if (paramsPosMapping.contains(statement.symbol.name)) {
885-
println("parameter "+statement)
886-
addOccurence(statement.symbol, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name))
875+
addOccurenceTree(statement, s.SymbolOccurrence.Role.DEFINITION, paramsPosMapping(statement.symbol.name))
887876
}
888-
//traverseTree(statement)
889877
} else if (!statement.symbol.flags.is(Flags.Param)) {
890-
println(statement.symbol, statement.symbol.flags)
891878
traverseTree(statement)
892879
}
893880
})
894-
895-
classStacks = classStacks.tail
896-
}
897-
898881
}
899882

900883
case DefDef("<init>", typeparams, valparams, type_, statements) if fittedInitClassRange != None => {
@@ -914,13 +897,12 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
914897

915898
case Term.Assign(lhs, rhs) => {
916899
isAssignedTerm = true
917-
super.traverseTree(tree)
900+
traverseTree(lhs)
918901
isAssignedTerm = false
902+
traverseTree(rhs)
919903
}
920904

921905
case IsDefinition(cdef) => {
922-
println("definition " + cdef.symbol + " " + cdef.symbol.flags)
923-
println(cdef.symbol.protectedWithin, cdef.symbol.privateWithin)
924906
if (cdef.symbol.flags.is(Flags.Protected)) {
925907
cdef.symbol.protectedWithin match {
926908
case Some(within) => {
@@ -939,7 +921,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
939921
} else {
940922
cdef.symbol.privateWithin match {
941923
case Some(within) => {
942-
println("YES")
943924
val startColumn = cdef.pos.startColumn + "private[".length
944925
addOccurence(
945926
within.typeSymbol,
@@ -978,10 +959,24 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
978959
super.traverseTree(cdef)
979960
}
980961

981-
case Term.This(Some(_)) => {
962+
case Term.This(Some(id)) => {
963+
/* We've got two options here:
964+
- either the this is explicit: eg C.this.XXX. In this case, the position is [C.this], but
965+
we want to put the symbol on the C, so around id
966+
- either it is not explicit (eg a.foo). We want to put the symbol only around the a.
967+
Distinguishing between the two is easy. If the sourcecode between [pos.start; pos.end] ends
968+
with a 'this', then we're in the first case, otherwise the second
969+
*/
970+
var rangeThis = posToRange(tree.pos).get
971+
if (sourceCode.substring(tree.pos.start, tree.pos.end).endsWith("this")) {
972+
rangeThis = range(tree, tree.pos, tree.symbol.trueName)
973+
}
974+
/*range = s.Range(tree.pos.startLine, tree.pos.startColumn,
975+
tree.pos.endLine,
976+
)*/
982977
addOccurenceTree(tree,
983978
s.SymbolOccurrence.Role.REFERENCE,
984-
posToRange(tree.pos).get)
979+
rangeThis)
985980
}
986981

987982
case Term.Super(_, Some(id)) =>
@@ -1001,14 +996,15 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
1001996
range = s.Range(tree.pos.startLine, tree.pos.start - 5, tree.pos.startLine, tree.pos.start - 1)
1002997
shouldForceAdd = true
1003998
} else {
999+
range = s.Range(tree.pos.endLine, tree.pos.endColumn, tree.pos.endLine, tree.pos.endColumn)
10041000
shouldForceAdd = qualifier.isUserCreated
10051001
}
10061002
}
10071003
val temp = isAssignedTerm
10081004
isAssignedTerm = false
10091005
super.traverseTree(tree)
10101006
isAssignedTerm = temp
1011-
addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable))
1007+
addOccurenceTree(tree, s.SymbolOccurrence.Role.REFERENCE, range, shouldForceAdd, isAssignedTerm && tree.symbol.flags.is(Flags.Mutable) && !tree.symbol.flags.is(Flags.PrivateLocal))
10121008
}
10131009

10141010
case Term.Ident(name) => {
@@ -1062,9 +1058,6 @@ class SemanticdbConsumer(sourceFile: java.nio.file.Path) extends TastyConsumer {
10621058
}
10631059

10641060
}
1065-
println("{--------------------------------------}")
1066-
println(root)
1067-
println("{--------------------------------------}")
10681061

10691062
Traverser.traverseTree(root)(reflect.rootContext)
10701063
}

semanticdb/test/dotty/semanticdb/Tests.scala

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,74 @@ import scala.tasty.Reflection
1414
import scala.tasty.file.TastyConsumer
1515
import java.lang.reflect.InvocationTargetException
1616

17+
import dotty.semanticdb.Scala._
18+
19+
1720
class Tests {
1821

22+
def min(a: Int, b: Int) : Int = if (a > b) b else a
23+
def max(a: Int, b: Int) : Int = if (a > b) a else b
24+
def abs(a: Int, b: Int) : Int = max(a, b) - min(a, b)
25+
def distance(r1 : s.Range, offsets : Array[Int])(r2 : s.Range) : Int = {
26+
val s1 = offsets(max(r1.startLine, 0)) + r1.startCharacter
27+
val s2 = offsets(max(r2.startLine, 0)) + r2.startCharacter
28+
val e1 = offsets(max(r1.endLine, 0)) + r1.endCharacter
29+
val e2 = offsets(max(r2.endLine, 0)) + r2.endCharacter
30+
max(abs(s1, s2), abs(e1, e2))
31+
}
32+
33+
def compareOccurences(tastyOccurences : Seq[s.SymbolOccurrence],
34+
scalaOccurences : Seq[s.SymbolOccurrence],
35+
sourceCode : String)
36+
: Boolean= {
37+
val lineToByte = sourceCode.split("\n").scanLeft(0)((o, l) => o + l.length + 1)
38+
val symbols = tastyOccurences.groupBy(_.symbol)
39+
val localTastyToScala = HashMap[String, String]()
40+
val localScalaToTasty = HashMap[String, String]()
41+
val translator = HashMap[(s.Range, String), s.SymbolOccurrence]()
42+
43+
// from is in tasty space, to in scala space
44+
def checkIfTranslatableSymbol(from : String, to : String) : Boolean = {
45+
if (from.isLocal != to.isLocal) {
46+
false
47+
} else {
48+
if (from.isLocal) {
49+
if(localTastyToScala.getOrElse(from, to) == to &&
50+
localScalaToTasty.getOrElse(to, from) == from) {
51+
localTastyToScala += (from -> to)
52+
localScalaToTasty += (to -> from)
53+
true
54+
} else {
55+
false
56+
}
57+
} else {
58+
true
59+
}
60+
}
61+
}
62+
63+
if (tastyOccurences.length != scalaOccurences.length) {
64+
false
65+
} else {
66+
scalaOccurences.forall(occurence => {
67+
if (symbols.contains(localScalaToTasty.getOrElse(occurence.symbol, occurence.symbol))) {
68+
val siblings = symbols(occurence.symbol)
69+
val nearest = siblings.minBy((c : s.SymbolOccurrence) => distance(occurence.range.get, lineToByte)(c.range.get))
70+
if (!checkIfTranslatableSymbol(nearest.symbol, occurence.symbol) ||
71+
translator.contains((nearest.range.get, nearest.symbol)) ||
72+
distance(occurence.range.get, lineToByte)(nearest.range.get) > 5) {
73+
false
74+
} else {
75+
translator += ((nearest.range.get, nearest.symbol) -> occurence)
76+
true
77+
}
78+
} else {
79+
false
80+
}
81+
})
82+
}
83+
}
84+
1985
// TODO: update scala-0.13 on version change (or resolve automatically)
2086
final def tastyClassDirectory =
2187
Paths.get("out/bootstrap/dotty-semanticdb/scala-0.12/test-classes/")
@@ -34,9 +100,6 @@ class Tests {
34100
/** Returns the SemanticDB for this Scala source file. */
35101
def getTastySemanticdb(classPath: Path, scalaFile: Path) : s.TextDocument = {
36102
val classNames = Utils.getClassNames(classPath, scalaFile, "example/")
37-
println(classPath)
38-
println(classNames)
39-
println(scalaFile)
40103
val sdbconsumer = new SemanticdbConsumer(scalaFile)
41104

42105
val _ = ConsumeTasty(classPath.toString, classNames, sdbconsumer)
@@ -48,11 +111,10 @@ class Tests {
48111
val path = sourceDirectory.resolve(filename)
49112
val scalac = getScalacSemanticdb(path)
50113
val tasty = getTastySemanticdb(tastyClassDirectory, path)
51-
println(tasty)
52114
val obtained = Semanticdbs.printTextDocument(tasty)
53115
val expected = Semanticdbs.printTextDocument(scalac)
54-
print("X=>",scalac.occurrences)
55-
assertNoDiff(obtained, expected)
116+
if (!compareOccurences(tasty.occurrences, scalac.occurrences, scalac.text))
117+
assertNoDiff(obtained, expected)
56118
}
57119

58120
/** Fails the test with a pretty diff if there obtained is not the same as expected */
@@ -83,7 +145,7 @@ class Tests {
83145
}
84146

85147

86-
/*@Test def testAccess(): Unit = checkFile("example/Access.scala")
148+
@Test def testAccess(): Unit = checkFile("example/Access.scala")
87149
@Test def testAdvanced(): Unit = checkFile("example/Advanced.scala")
88150
@Test def testAnonymous(): Unit = checkFile("example/Anonymous.scala")
89151
@Test def testClasses(): Unit = checkFile("example/Classes.scala")
@@ -119,12 +181,5 @@ class Tests {
119181
@Test def testSynthetic(): Unit = checkFile("example/Synthetic.scala")
120182
@Test def testBinaryOp(): Unit = checkFile("example/BinaryOp.scala")
121183
@Test def testDottyPredef(): Unit = checkFile("example/DottyPredef.scala")
122-
*/
123-
//@Test def testTypesAnnotations() : Unit = checkFile("example/TypesAnnotations.scala") // Crash, has to deal with init symbols
124-
//@Test def testNew(): Unit = checkFile("example/New.scala")
125-
//@Test def testClasses(): Unit = checkFile("example/Classes.scala")
126184

127-
//@Test def testSemanticDoc(): Unit = checkFile("example/SemanticDoc.scala")
128-
@Test def testAccess(): Unit = checkFile("example/Access.scala")
129-
//@Test def testDependantModule(): Unit = checkFile("example/DependantModule.scala")
130185
}

0 commit comments

Comments
 (0)