Skip to content

Commit d463a9e

Browse files
authored
Merge pull request #8983 from dotty-staging/fix-#8970
compile dotty with Ysemanticdb
2 parents 348184f + bd906c4 commit d463a9e

28 files changed

+847
-298
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ class Compiler {
146146

147147
def newRun(implicit ctx: Context): Run = {
148148
reset()
149-
new Run(this, ctx)
149+
val rctx =
150+
if ctx.settings.Ysemanticdb.value
151+
ctx.addMode(Mode.ReadPositions)
152+
else
153+
ctx
154+
new Run(this, rctx)
150155
}
151156
}

compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala

Lines changed: 83 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ExtractSemanticDB extends Phase:
6464
val occurrences = new mutable.ListBuffer[SymbolOccurrence]()
6565

6666
/** The extracted symbol infos */
67-
val symbolInfos = new mutable.HashSet[SymbolInformation]()
67+
val symbolInfos = new mutable.ListBuffer[SymbolInformation]()
6868

6969
/** A cache of localN names */
7070
val localNames = new mutable.HashSet[String]()
@@ -81,30 +81,36 @@ class ExtractSemanticDB extends Phase:
8181
|| excludeDefOrUse(sym)
8282

8383
private def excludeDefOrUse(sym: Symbol)(using Context): Boolean =
84-
sym.name.is(NameKinds.DefaultGetterName)
84+
!sym.exists
85+
|| sym.name.is(NameKinds.DefaultGetterName)
8586
|| sym.isConstructor && (sym.owner.is(ModuleClass) || !sym.isGlobal)
8687
|| excludeSymbol(sym)
8788

8889
private def excludeSymbol(sym: Symbol)(using Context): Boolean =
89-
sym.name.isWildcard
90+
!sym.exists
91+
|| sym.name.isWildcard
9092
|| excludeQual(sym)
9193

9294
private def excludeQual(sym: Symbol)(using Context): Boolean =
93-
sym.isAnonymousFunction
95+
!sym.exists
96+
|| sym.isAnonymousFunction
9497
|| sym.isAnonymousModuleVal
9598
|| sym.name.isEmptyNumbered
9699

97100
private def excludeChildren(sym: Symbol)(using Context): Boolean =
98-
sym.isAllOf(HigherKinded | Param)
101+
!sym.exists
102+
|| sym.isAllOf(HigherKinded | Param)
99103

100104
/** Uses of this symbol where the reference has given span should be excluded from semanticdb */
101105
private def excludeUse(qualifier: Option[Symbol], sym: Symbol)(using Context): Boolean =
102-
excludeDefOrUse(sym)
106+
!sym.exists
107+
|| excludeDefOrUse(sym)
103108
|| sym.isConstructor && sym.owner.isAnnotation
104109
|| sym == defn.Any_typeCast
110+
|| sym.owner == defn.OpsPackageClass
105111
|| qualifier.exists(excludeQual)
106112

107-
private def traverseAnnotsOf(sym: Symbol)(using Context): Unit =
113+
private def traverseAnnotsOfDefinition(sym: Symbol)(using Context): Unit =
108114
for annot <- sym.annotations do
109115
if annot.tree.span.exists
110116
&& annot.tree.span.hasLength
@@ -114,36 +120,31 @@ class ExtractSemanticDB extends Phase:
114120

115121
override def traverse(tree: Tree)(using Context): Unit =
116122

117-
inline def traverseCtorParamTpt(ctorSym: Symbol, tpt: Tree): Unit =
118-
val tptSym = tpt.symbol
119-
if tptSym.owner == ctorSym
120-
val found = matchingMemberType(tptSym, ctorSym.owner)
121-
if tpt.span.hasLength
122-
registerUseGuarded(None, found, tpt.span)
123-
else
124-
traverse(tpt)
125-
126-
traverseAnnotsOf(tree.symbol)
123+
tree match
124+
case tree: DefTree if tree.symbol.exists =>
125+
traverseAnnotsOfDefinition(tree.symbol)
126+
case _ =>
127+
()
127128

128129
tree match
129130
case tree: PackageDef =>
130131
if !excludeDef(tree.pid.symbol)
131132
&& tree.pid.span.hasLength
132133
tree.pid match
133-
case tree @ Select(qual, name) =>
134-
registerDefinition(tree.symbol, adjustSpanToName(tree.span, qual.span, name), Set.empty)
135-
traverse(qual)
136-
case tree => registerDefinition(tree.symbol, tree.span, Set.empty)
134+
case tree: Select =>
135+
registerDefinition(tree.symbol, selectSpan(tree), Set.empty, tree.source)
136+
traverse(tree.qualifier)
137+
case tree => registerDefinition(tree.symbol, tree.span, Set.empty, tree.source)
137138
tree.stats.foreach(traverse)
138139
case tree: NamedDefTree =>
139140
if tree.symbol.isAllOf(ModuleValCreationFlags)
140141
return
141142
if !excludeDef(tree.symbol)
142143
&& tree.span.hasLength
143-
registerDefinition(tree.symbol, tree.adjustedNameSpan, symbolKinds(tree))
144+
registerDefinition(tree.symbol, tree.adjustedNameSpan, symbolKinds(tree), tree.source)
144145
val privateWithin = tree.symbol.privateWithin
145146
if privateWithin.exists
146-
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span))
147+
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source)
147148
else if !excludeSymbol(tree.symbol)
148149
registerSymbol(tree.symbol, symbolName(tree.symbol), symbolKinds(tree))
149150
tree match
@@ -180,8 +181,9 @@ class ExtractSemanticDB extends Phase:
180181
case tree: Template =>
181182
val ctorSym = tree.constr.symbol
182183
if !excludeDef(ctorSym)
183-
registerDefinition(ctorSym, tree.constr.span, Set.empty)
184-
ctorParams(tree.constr.vparamss, tree.body)(traverseCtorParamTpt(ctorSym, _))
184+
traverseAnnotsOfDefinition(ctorSym)
185+
registerDefinition(ctorSym, tree.constr.span, Set.empty, tree.source)
186+
ctorParams(tree.constr.vparamss, tree.body)
185187
for parent <- tree.parentsOrDerived if parent.span.hasLength do
186188
traverse(parent)
187189
val selfSpan = tree.self.span
@@ -196,39 +198,40 @@ class ExtractSemanticDB extends Phase:
196198
traverse(tree.fun)
197199
for arg <- tree.args do
198200
arg match
199-
case arg @ NamedArg(name, value) =>
200-
registerUse(genParamSymbol(name), arg.span.startPos.withEnd(arg.span.start + name.toString.length))
201-
traverse(localBodies.get(value.symbol).getOrElse(value))
201+
case tree @ NamedArg(name, arg) =>
202+
registerUse(genParamSymbol(name), tree.span.startPos.withEnd(tree.span.start + name.toString.length), tree.source)
203+
traverse(localBodies.get(arg.symbol).getOrElse(arg))
202204
case _ => traverse(arg)
203205
case tree: Assign =>
204206
val qualSym = condOpt(tree.lhs) { case Select(qual, _) if qual.symbol.exists => qual.symbol }
205207
if !excludeUse(qualSym, tree.lhs.symbol)
206208
val lhs = tree.lhs.symbol
207209
val setter = lhs.matchingSetter.orElse(lhs)
208210
tree.lhs match
209-
case tree @ Select(qual, name) => registerUse(setter, adjustSpanToName(tree.span, qual.span, name))
210-
case tree => registerUse(setter, tree.span)
211+
case tree: Select => registerUse(setter, selectSpan(tree), tree.source)
212+
case tree => registerUse(setter, tree.span, tree.source)
211213
traverseChildren(tree.lhs)
212214
traverse(tree.rhs)
213215
case tree: Ident =>
214216
if tree.name != nme.WILDCARD then
215217
val sym = tree.symbol.adjustIfCtorTyparam
216-
registerUseGuarded(None, sym, tree.span)
218+
registerUseGuarded(None, sym, tree.span, tree.source)
217219
case tree: Select =>
218-
val qualSpan = tree.qualifier.span
220+
val qual = tree.qualifier
221+
val qualSpan = qual.span
219222
val sym = tree.symbol.adjustIfCtorTyparam
220-
registerUseGuarded(tree.qualifier.symbol.ifExists, sym, adjustSpanToName(tree.span, qualSpan, tree.name))
223+
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
221224
if qualSpan.exists && qualSpan.hasLength then
222-
traverse(tree.qualifier)
225+
traverse(qual)
223226
case tree: Import =>
224227
if tree.span.exists && tree.span.hasLength then
225228
for sel <- tree.selectors do
226229
val imported = sel.imported.name
227230
if imported != nme.WILDCARD then
228231
for alt <- tree.expr.tpe.member(imported).alternatives do
229-
registerUseGuarded(None, alt.symbol, sel.imported.span)
232+
registerUseGuarded(None, alt.symbol, sel.imported.span, tree.source)
230233
if (alt.symbol.companionClass.exists)
231-
registerUseGuarded(None, alt.symbol.companionClass, sel.imported.span)
234+
registerUseGuarded(None, alt.symbol.companionClass, sel.imported.span, tree.source)
232235
traverseChildren(tree)
233236
case tree: Inlined =>
234237
traverse(tree.call)
@@ -302,12 +305,15 @@ class ExtractSemanticDB extends Phase:
302305
else
303306
decls0
304307
end decls
305-
val alts = decls.filter(_.is(Method)).toList.reverse
306-
alts match
307-
case notSym :: rest if sym != notSym =>
308-
val idx = rest.indexOf(sym).ensuring(_ >= 0)
309-
b.append('+').append(idx + 1)
310-
case _ =>
308+
val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse
309+
def find(filter: Symbol => Boolean) = alts match
310+
case notSym :: rest if !filter(notSym) =>
311+
val idx = rest.indexWhere(filter).ensuring(_ >= 0)
312+
b.append('+').append(idx + 1)
313+
case _ =>
314+
end find
315+
val sig = sym.signature
316+
find(_.signature == sig)
311317

312318
def addDescriptor(sym: Symbol): Unit =
313319
if sym.is(ModuleClass) then
@@ -335,16 +341,23 @@ class ExtractSemanticDB extends Phase:
335341
* the same starting position have the same index.
336342
*/
337343
def localIdx(sym: Symbol)(using Context): Int =
338-
def computeLocalIdx(): Int =
339-
symsAtOffset(sym.span.start).find(_.name == sym.name) match
340-
case Some(other) => localIdx(other)
344+
val startPos =
345+
assert(sym.span.exists, s"$sym should have a span")
346+
sym.span.start
347+
@tailrec
348+
def computeLocalIdx(sym: Symbol): Int = locals get sym match
349+
case Some(idx) => idx
350+
case None => symsAtOffset(startPos).find(_.name == sym.name) match
351+
case Some(other) => computeLocalIdx(other)
341352
case None =>
342353
val idx = nextLocalIdx
343354
nextLocalIdx += 1
344355
locals(sym) = idx
345-
symsAtOffset(sym.span.start) += sym
356+
symsAtOffset(startPos) += sym
346357
idx
347-
locals.getOrElseUpdate(sym, computeLocalIdx())
358+
end computeLocalIdx
359+
computeLocalIdx(sym)
360+
end localIdx
348361

349362
if sym.exists then
350363
if sym.isGlobal then
@@ -360,10 +373,8 @@ class ExtractSemanticDB extends Phase:
360373
addSymName(b, sym)
361374
b.toString
362375

363-
inline private def source(using Context) = ctx.compilationUnit.source
364-
365-
private def range(span: Span)(using Context): Option[Range] =
366-
def lineCol(offset: Int) = (source.offsetToLine(offset), source.column(offset))
376+
private def range(span: Span, treeSource: SourceFile)(using Context): Option[Range] =
377+
def lineCol(offset: Int) = (treeSource.offsetToLine(offset), treeSource.column(offset))
367378
val (startLine, startCol) = lineCol(span.start)
368379
val (endLine, endCol) = lineCol(span.end)
369380
Some(Range(startLine, startCol, endLine, endCol))
@@ -455,30 +466,30 @@ class ExtractSemanticDB extends Phase:
455466
private def registerSymbolSimple(sym: Symbol)(using Context): Unit =
456467
registerSymbol(sym, symbolName(sym), Set.empty)
457468

458-
private def registerOccurrence(symbol: String, span: Span, role: SymbolOccurrence.Role)(using Context): Unit =
459-
val occ = SymbolOccurrence(symbol, range(span), role)
469+
private def registerOccurrence(symbol: String, span: Span, role: SymbolOccurrence.Role, treeSource: SourceFile)(using Context): Unit =
470+
val occ = SymbolOccurrence(symbol, range(span, treeSource), role)
460471
if !generated.contains(occ) && occ.symbol.nonEmpty then
461472
occurrences += occ
462473
generated += occ
463474

464-
private def registerUseGuarded(qualSym: Option[Symbol], sym: Symbol, span: Span)(using Context) =
475+
private def registerUseGuarded(qualSym: Option[Symbol], sym: Symbol, span: Span, treeSource: SourceFile)(using Context) =
465476
if !excludeUse(qualSym, sym) then
466-
registerUse(sym, span)
477+
registerUse(sym, span, treeSource)
467478

468-
private def registerUse(sym: Symbol, span: Span)(using Context): Unit =
469-
registerUse(symbolName(sym), span)
479+
private def registerUse(sym: Symbol, span: Span, treeSource: SourceFile)(using Context): Unit =
480+
registerUse(symbolName(sym), span, treeSource)
470481

471-
private def registerUse(symbol: String, span: Span)(using Context): Unit =
472-
registerOccurrence(symbol, span, SymbolOccurrence.Role.REFERENCE)
482+
private def registerUse(symbol: String, span: Span, treeSource: SourceFile)(using Context): Unit =
483+
registerOccurrence(symbol, span, SymbolOccurrence.Role.REFERENCE, treeSource)
473484

474-
private def registerDefinition(sym: Symbol, span: Span, symkinds: Set[SymbolKind])(using Context) =
485+
private def registerDefinition(sym: Symbol, span: Span, symkinds: Set[SymbolKind], treeSource: SourceFile)(using Context) =
475486
val symbol = symbolName(sym)
476-
registerOccurrence(symbol, span, SymbolOccurrence.Role.DEFINITION)
487+
registerOccurrence(symbol, span, SymbolOccurrence.Role.DEFINITION, treeSource)
477488
if !sym.is(Package)
478489
registerSymbol(sym, symbol, symkinds)
479490

480-
private def spanOfSymbol(sym: Symbol, span: Span)(using Context): Span =
481-
val contents = if source.exists then source.content() else Array.empty[Char]
491+
private def spanOfSymbol(sym: Symbol, span: Span, treeSource: SourceFile)(using Context): Span =
492+
val contents = if treeSource.exists then treeSource.content() else Array.empty[Char]
482493
val idx = contents.indexOfSlice(sym.name.show, span.start)
483494
val start = if idx >= 0 then idx else span.start
484495
Span(start, start + sym.name.show.length, start)
@@ -502,13 +513,13 @@ class ExtractSemanticDB extends Phase:
502513
}).toMap
503514
end findGetters
504515

505-
private def adjustSpanToName(span: Span, qualSpan: Span, name: Name)(using Context) =
506-
val end = span.end
507-
val limit = qualSpan.end
516+
private def selectSpan(tree: Select) =
517+
val end = tree.span.end
518+
val limit = tree.qualifier.span.end
508519
val start =
509520
if limit < end then
510-
val len = name.toString.length
511-
if source.content()(end - 1) == '`' then end - len - 2 else end - len
521+
val len = tree.name.toString.length
522+
if tree.source.content()(end - 1) == '`' then end - len - 2 else end - len
512523
else limit
513524
Span(start max limit, end)
514525

@@ -559,20 +570,20 @@ class ExtractSemanticDB extends Phase:
559570
case _ =>
560571
symkinds.toSet
561572

562-
private inline def ctorParams(
563-
vparamss: List[List[ValDef]], body: List[Tree])(traverseTpt: => Tree => Unit)(using Context): Unit =
573+
private def ctorParams(
574+
vparamss: List[List[ValDef]], body: List[Tree])(using Context): Unit =
564575
@tu lazy val getters = findGetters(vparamss.flatMap(_.map(_.name)).toSet, body)
565576
for
566577
vparams <- vparamss
567578
vparam <- vparams
568579
do
569-
traverseAnnotsOf(vparam.symbol)
570580
if !excludeSymbol(vparam.symbol)
581+
traverseAnnotsOfDefinition(vparam.symbol)
571582
val symkinds =
572583
getters.get(vparam.name).fold(SymbolKind.emptySet)(getter =>
573584
if getter.mods.is(Mutable) then SymbolKind.VarSet else SymbolKind.ValSet)
574585
registerSymbol(vparam.symbol, symbolName(vparam.symbol), symkinds)
575-
traverseTpt(vparam.tpt)
586+
traverse(vparam.tpt)
576587

577588
object ExtractSemanticDB:
578589
import java.nio.file.Path

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class CompilationTests extends ParallelTesting {
4949
compileFilesInDir("tests/new", defaultOptions),
5050
compileFilesInDir("tests/pos-scala2", scala2CompatMode),
5151
compileFilesInDir("tests/pos-custom-args/erased", defaultOptions.and("-Yerased-terms")),
52+
compileFilesInDir("tests/pos-custom-args/semanticdb", defaultOptions.and("-Ysemanticdb")),
5253
compileFilesInDir("tests/pos", defaultOptions),
5354
compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes),
5455
compileFile(
@@ -201,9 +202,9 @@ class CompilationTests extends ParallelTesting {
201202
).checkCompile()
202203
}
203204

204-
/** The purpose of this test is two-fold, being able to compile dotty
205+
/** The purpose of this test is three-fold, being able to compile dotty
205206
* bootstrapped, and making sure that TASTY can link against a compiled
206-
* version of Dotty
207+
* version of Dotty, and compiling the compiler using the SemanticDB generation
207208
*/
208209
@Test def tastyBootstrap: Unit = {
209210
implicit val testGroup: TestGroup = TestGroup("tastyBootstrap/tests")
@@ -227,7 +228,7 @@ class CompilationTests extends ParallelTesting {
227228
Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm,
228229
Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader,
229230
).mkString(File.pathSeparator),
230-
Array("-Ycheck-reentrant", "-Yemit-tasty-in-class", "-language:postfixOps")
231+
Array("-Ycheck-reentrant", "-Yemit-tasty-in-class", "-language:postfixOps", "-Ysemanticdb")
231232
)
232233

233234
val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped"))

compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class SemanticdbTests:
7373
println(s"""[${red("error")}] check file ${blue(expect.toString)} does not match generated.
7474
|If you meant to make a change, replace the expect file by:
7575
| mv ${expect.resolveSibling("" + expect.getFileName + ".out")} $expect
76+
|inspect with:
77+
| diff $expect ${expect.resolveSibling("" + expect.getFileName + ".out")}
7678
|Or else update all expect files with
7779
| sbt 'dotty-compiler-bootstrapped/test:runMain dotty.tools.dotc.semanticdb.updateExpect'""".stripMargin)
7880
Files.walk(target).sorted(Comparator.reverseOrder).forEach(Files.delete)

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ object TestConfiguration {
1616
"-Yno-deep-subtypes",
1717
"-Yno-double-bindings",
1818
"-Yforce-sbt-phases",
19+
"-Ysemanticdb",
1920
"-Xverify-signatures"
2021
)
2122

tests/neg/mixin-forwarder-clash2.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
1 |class Bar2 extends Bar1 with Two[Foo] // error
44
| ^
55
| Name clash between inherited members:
6-
| def concat(suffix: Int): X in trait One and
7-
| def concat: [Dummy](suffix: Int): Y in trait Two
6+
| def concat(suffix: Int): X in trait One at line 4 and
7+
| def concat: [Dummy](suffix: Int): Y in trait Two at line 8
88
| have the same type after erasure.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
0 match
5+
case Succ(n) => ???
6+
case _ =>
7+
8+
2 match
9+
case Succ(n) => assert(n == 1)
10+
}
11+
12+
}

0 commit comments

Comments
 (0)