Skip to content

Commit 85575cc

Browse files
authored
Merge pull request #12541 from dotty-staging/fix-12326
record end markers in trees and semanticdb, exclude top level def wrappers
2 parents fa36577 + c327d79 commit 85575cc

19 files changed

+385
-80
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ object desugar {
182182
tpt = TypeTree(defn.UnitType),
183183
rhs = setterRhs
184184
).withMods((mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
185+
.dropEndMarker() // the end marker should only appear on the getter definition
185186
Thicket(vdef1, setter)
186187
}
187188
else vdef1
@@ -883,6 +884,7 @@ object desugar {
883884
val clsTmpl = cpy.Template(impl)(self = clsSelf, body = impl.body)
884885
val cls = TypeDef(clsName, clsTmpl)
885886
.withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags)
887+
.withEndMarker(copyFrom = mdef) // copy over the end marker position to the module class def
886888
Thicket(modul, classDef(cls).withSpan(mdef.span))
887889
}
888890
}
@@ -1299,7 +1301,9 @@ object desugar {
12991301
if (nestedStats.isEmpty) pdef
13001302
else {
13011303
val name = packageObjectName(ctx.source)
1302-
val grouped = ModuleDef(name, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
1304+
val grouped =
1305+
ModuleDef(name, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
1306+
.withMods(Modifiers(Synthetic))
13031307
cpy.PackageDef(pdef)(pdef.pid, topStats :+ grouped)
13041308
}
13051309
}

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

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,58 @@ object Trees {
327327

328328
extension (mdef: untpd.DefTree) def mods: untpd.Modifiers = mdef.rawMods
329329

330-
abstract class NamedDefTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] {
330+
sealed trait WithEndMarker[-T >: Untyped]:
331+
self: PackageDef[T] | NamedDefTree[T] =>
332+
333+
import WithEndMarker.*
334+
335+
final def endSpan(using Context): Span =
336+
if hasEndMarker then
337+
val realName = srcName.stripModuleClassSuffix.lastPart
338+
span.withStart(span.end - realName.length)
339+
else
340+
NoSpan
341+
342+
/** The name in source code that represents this construct,
343+
* and is the name that the user must write to create a valid
344+
* end marker.
345+
* e.g. a constructor definition is terminated in the source
346+
* code by `end this`, so it's `srcName` should return `this`.
347+
*/
348+
protected def srcName(using Context): Name
349+
350+
final def withEndMarker(): self.type =
351+
self.withAttachment(HasEndMarker, ())
352+
353+
final def withEndMarker(copyFrom: WithEndMarker[?]): self.type =
354+
if copyFrom.hasEndMarker then
355+
this.withEndMarker()
356+
else
357+
this
358+
359+
final def dropEndMarker(): self.type =
360+
self.removeAttachment(HasEndMarker)
361+
this
362+
363+
protected def hasEndMarker: Boolean = self.hasAttachment(HasEndMarker)
364+
365+
object WithEndMarker:
366+
/** Property key that signals the tree was terminated
367+
* with an `end` marker in the source code
368+
*/
369+
private val HasEndMarker: Property.StickyKey[Unit] = Property.StickyKey()
370+
371+
end WithEndMarker
372+
373+
abstract class NamedDefTree[-T >: Untyped](implicit @constructorOnly src: SourceFile)
374+
extends NameTree[T] with DefTree[T] with WithEndMarker[T] {
331375
type ThisTree[-T >: Untyped] <: NamedDefTree[T]
332376

377+
protected def srcName(using Context): Name =
378+
if name == nme.CONSTRUCTOR then nme.this_
379+
else if symbol.isPackageObject then symbol.owner.name
380+
else name
381+
333382
/** The position of the name defined by this definition.
334383
* This is a point position if the definition is synthetic, or a range position
335384
* if the definition comes from source.
@@ -342,7 +391,7 @@ object Trees {
342391
val point = span.point
343392
if (rawMods.is(Synthetic) || span.isSynthetic || name.toTermName == nme.ERROR) Span(point)
344393
else {
345-
val realName = name.stripModuleClassSuffix.lastPart
394+
val realName = srcName.stripModuleClassSuffix.lastPart
346395
Span(point, point + realName.length, point)
347396
}
348397
}
@@ -857,9 +906,10 @@ object Trees {
857906

858907
/** package pid { stats } */
859908
case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
860-
extends ProxyTree[T] {
909+
extends ProxyTree[T] with WithEndMarker[T] {
861910
type ThisTree[-T >: Untyped] = PackageDef[T]
862911
def forwardTo: RefTree[T] = pid
912+
protected def srcName(using Context): Name = pid.name
863913
}
864914

865915
/** arg @annot */

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,16 +1275,20 @@ object Parsers {
12751275

12761276
def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit =
12771277

1278-
def matches(stat: Tree): Boolean = stat match
1278+
def updateSpanOfLast(last: T): Unit =
1279+
last match
1280+
case last: WithEndMarker[t] => last.withEndMarker()
1281+
case _ =>
1282+
last.span = last.span.withEnd(in.lastCharOffset)
1283+
1284+
def matches(stat: T): Boolean = stat match
12791285
case stat: MemberDef if !stat.name.isEmpty =>
12801286
if stat.name == nme.CONSTRUCTOR then in.token == THIS
12811287
else in.isIdent && in.name == stat.name.toTermName
12821288
case ExtMethods(_, _) =>
12831289
in.token == IDENTIFIER && in.name == nme.extension
12841290
case PackageDef(pid: RefTree, _) =>
12851291
in.isIdent && in.name == pid.name
1286-
case PatDef(_, IdPattern(id, _) :: Nil, _, _) =>
1287-
in.isIdent && in.name == id.name
12881292
case stat: MemberDef if stat.mods.is(Given) => in.token == GIVEN
12891293
case _: PatDef => in.token == VAL
12901294
case _: If => in.token == IF
@@ -1295,9 +1299,16 @@ object Parsers {
12951299
case _: (ForYield | ForDo) => in.token == FOR
12961300
case _ => false
12971301

1302+
def matchesAndSetEnd(last: T): Boolean = {
1303+
val didMatch = matches(last)
1304+
if didMatch then
1305+
updateSpanOfLast(last)
1306+
didMatch
1307+
}
1308+
12981309
if in.token == END then
12991310
val start = in.skipToken()
1300-
if stats.isEmpty || !matches(stats.last) then
1311+
if stats.isEmpty || !matchesAndSetEnd(stats.last) then
13011312
syntaxError("misaligned end marker", Span(start, in.lastCharOffset))
13021313
in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion
13031314
in.nextToken()

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

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import core._
66
import Phases._
77
import ast.tpd._
88
import ast.untpd.given
9-
import ast.Trees.mods
9+
import ast.Trees.{mods, WithEndMarker}
1010
import Contexts._
1111
import Symbols._
1212
import Flags._
@@ -139,47 +139,46 @@ class ExtractSemanticDB extends Phase:
139139
case tree => registerDefinition(tree.symbol, tree.span, Set.empty, tree.source)
140140
tree.stats.foreach(traverse)
141141
case tree: NamedDefTree =>
142-
if tree.symbol.isAllOf(ModuleValCreationFlags) then
143-
return
144-
if !excludeDef(tree.symbol)
145-
&& tree.span.hasLength then
146-
registerDefinition(tree.symbol, tree.nameSpan, symbolKinds(tree), tree.source)
147-
val privateWithin = tree.symbol.privateWithin
148-
if privateWithin.exists then
149-
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source)
150-
else if !excludeSymbol(tree.symbol) then
151-
registerSymbol(tree.symbol, symbolName(tree.symbol), symbolKinds(tree))
152-
tree match
153-
case tree: ValDef
154-
if tree.symbol.isAllOf(EnumValue) =>
155-
tree.rhs match
156-
case Block(TypeDef(_, template: Template) :: _, _) => // simple case with specialised extends clause
157-
template.parents.filter(!_.span.isZeroExtent).foreach(traverse)
158-
case _ => // calls $new
159-
case tree: ValDef
160-
if tree.symbol.isSelfSym =>
161-
if tree.tpt.span.hasLength then
162-
traverse(tree.tpt)
163-
case tree: DefDef
164-
if tree.symbol.isConstructor => // ignore typeparams for secondary ctors
165-
tree.trailingParamss.foreach(_.foreach(traverse))
166-
traverse(tree.rhs)
167-
case tree: (DefDef | ValDef)
168-
if tree.symbol.isSyntheticWithIdent =>
142+
if !tree.symbol.isAllOf(ModuleValCreationFlags) then
143+
if !excludeDef(tree.symbol)
144+
&& tree.span.hasLength then
145+
registerDefinition(tree.symbol, tree.nameSpan, symbolKinds(tree), tree.source)
146+
val privateWithin = tree.symbol.privateWithin
147+
if privateWithin.exists then
148+
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source)
149+
else if !excludeSymbol(tree.symbol) then
150+
registerSymbol(tree.symbol, symbolName(tree.symbol), symbolKinds(tree))
169151
tree match
170-
case tree: DefDef =>
171-
tree.paramss.foreach(_.foreach(param => registerSymbolSimple(param.symbol)))
172-
case tree: ValDef if tree.symbol.is(Given) => traverse(tree.tpt)
173-
case _ =>
174-
if !tree.symbol.isGlobal then
175-
localBodies(tree.symbol) = tree.rhs
176-
// ignore rhs
177-
case PatternValDef(pat, rhs) =>
178-
traverse(rhs)
179-
PatternValDef.collectPats(pat).foreach(traverse)
180-
case tree =>
181-
if !excludeChildren(tree.symbol) then
182-
traverseChildren(tree)
152+
case tree: ValDef
153+
if tree.symbol.isAllOf(EnumValue) =>
154+
tree.rhs match
155+
case Block(TypeDef(_, template: Template) :: _, _) => // simple case with specialised extends clause
156+
template.parents.filter(!_.span.isZeroExtent).foreach(traverse)
157+
case _ => // calls $new
158+
case tree: ValDef
159+
if tree.symbol.isSelfSym =>
160+
if tree.tpt.span.hasLength then
161+
traverse(tree.tpt)
162+
case tree: DefDef
163+
if tree.symbol.isConstructor => // ignore typeparams for secondary ctors
164+
tree.trailingParamss.foreach(_.foreach(traverse))
165+
traverse(tree.rhs)
166+
case tree: (DefDef | ValDef)
167+
if tree.symbol.isSyntheticWithIdent =>
168+
tree match
169+
case tree: DefDef =>
170+
tree.paramss.foreach(_.foreach(param => registerSymbolSimple(param.symbol)))
171+
case tree: ValDef if tree.symbol.is(Given) => traverse(tree.tpt)
172+
case _ =>
173+
if !tree.symbol.isGlobal then
174+
localBodies(tree.symbol) = tree.rhs
175+
// ignore rhs
176+
case PatternValDef(pat, rhs) =>
177+
traverse(rhs)
178+
PatternValDef.collectPats(pat).foreach(traverse)
179+
case tree =>
180+
if !excludeChildren(tree.symbol) then
181+
traverseChildren(tree)
183182
case tree: Template =>
184183
val ctorSym = tree.constr.symbol
185184
if !excludeDef(ctorSym) then
@@ -240,6 +239,13 @@ class ExtractSemanticDB extends Phase:
240239
case _ =>
241240
traverseChildren(tree)
242241

242+
tree match
243+
case tree: WithEndMarker[t] =>
244+
val endSpan = tree.endSpan
245+
if endSpan.exists then
246+
registerUseGuarded(None, tree.symbol, endSpan, tree.source)
247+
case _ =>
248+
243249
end traverse
244250

245251
private def funParamSymbol(funSym: Symbol)(using Context): Name => String =

tests/semanticdb/expect/Annotations.expect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Annotations/*<-annot::Annotations#*/[@TypeParameterAnnotation/*->com::java
2121

2222
class B/*<-annot::B#*/ @ConstructorAnnotation/*->com::javacp::annot::ConstructorAnnotation#*/()(x/*<-annot::B#x.*/: Int/*->scala::Int#*/) {
2323
@ConstructorAnnotation/*->com::javacp::annot::ConstructorAnnotation#*/
24-
def this()/*<-annot::B#`<init>`(+1).*/ = this(42)
24+
def this/*<-annot::B#`<init>`(+1).*/() = this(42)
2525
}
2626

2727
@ObjectAnnotation/*->com::javacp::annot::ObjectAnnotation#*/

tests/semanticdb/expect/AnonymousGiven.expect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ package angiven
22

33
trait Foo/*<-angiven::Foo#*/
44

5-
/*<-angiven::AnonymousGiven$package.*/def bar/*<-angiven::AnonymousGiven$package.bar().*/(using Foo/*->angiven::Foo#*/) = 42
5+
def bar/*<-angiven::AnonymousGiven$package.bar().*/(using Foo/*->angiven::Foo#*/) = 42
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package endmarkers:
2+
3+
class MultiCtor/*<-endmarkers::MultiCtor#*/(val i/*<-endmarkers::MultiCtor#i.*/: Int/*->scala::Int#*/):
4+
def this/*<-endmarkers::MultiCtor#`<init>`(+1).*/() =
5+
this(23)
6+
end this/*->endmarkers::MultiCtor#`<init>`(+1).*/
7+
end MultiCtor/*->endmarkers::MultiCtor#*/
8+
9+
def topLevelMethod/*<-endmarkers::EndMarkers$package.topLevelMethod().*/: String/*->scala::Predef.String#*/ =
10+
"hello"
11+
end topLevelMethod/*->endmarkers::EndMarkers$package.topLevelMethod().*/
12+
13+
val topLevelVal/*<-endmarkers::EndMarkers$package.topLevelVal.*/: Int/*->scala::Int#*/ =
14+
23
15+
end topLevelVal/*->endmarkers::EndMarkers$package.topLevelVal.*/
16+
17+
var topLevelVar/*<-endmarkers::EndMarkers$package.topLevelVar().*/: String/*->scala::Predef.String#*/ =
18+
""
19+
end topLevelVar/*->endmarkers::EndMarkers$package.topLevelVar().*/
20+
21+
class Container/*<-endmarkers::Container#*/:
22+
23+
def foo/*<-endmarkers::Container#foo().*/ =
24+
(/*->scala::Tuple3.apply().*/1,2,3)
25+
end foo/*->endmarkers::Container#foo().*/
26+
27+
val bar/*<-endmarkers::Container#bar.*/ =
28+
(/*->scala::Tuple3.apply().*/4,5,6)
29+
end bar/*->endmarkers::Container#bar.*/
30+
31+
var baz/*<-endmarkers::Container#baz().*/ =
32+
15
33+
end baz/*->endmarkers::Container#baz().*/
34+
35+
end Container/*->endmarkers::Container#*/
36+
37+
def topLevelWithLocals/*<-endmarkers::EndMarkers$package.topLevelWithLocals().*/: Unit/*->scala::Unit#*/ =
38+
39+
val localVal/*<-local0*/ =
40+
37
41+
end localVal/*->local0*/
42+
43+
var localVar/*<-local1*/ =
44+
43
45+
end localVar/*->local1*/
46+
47+
def localDef/*<-local2*/ =
48+
97
49+
end localDef/*->local2*/
50+
51+
end topLevelWithLocals/*->endmarkers::EndMarkers$package.topLevelWithLocals().*/
52+
53+
object TestObj/*<-endmarkers::TestObj.*/:
54+
55+
def foo/*<-endmarkers::TestObj.foo().*/ = 23
56+
57+
end TestObj/*->endmarkers::TestObj.*/
58+
59+
end endmarkers
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package endmarkers:
2+
3+
class MultiCtor(val i: Int):
4+
def this() =
5+
this(23)
6+
end this
7+
end MultiCtor
8+
9+
def topLevelMethod: String =
10+
"hello"
11+
end topLevelMethod
12+
13+
val topLevelVal: Int =
14+
23
15+
end topLevelVal
16+
17+
var topLevelVar: String =
18+
""
19+
end topLevelVar
20+
21+
class Container:
22+
23+
def foo =
24+
(1,2,3)
25+
end foo
26+
27+
val bar =
28+
(4,5,6)
29+
end bar
30+
31+
var baz =
32+
15
33+
end baz
34+
35+
end Container
36+
37+
def topLevelWithLocals: Unit =
38+
39+
val localVal =
40+
37
41+
end localVal
42+
43+
var localVar =
44+
43
45+
end localVar
46+
47+
def localDef =
48+
97
49+
end localDef
50+
51+
end topLevelWithLocals
52+
53+
object TestObj:
54+
55+
def foo = 23
56+
57+
end TestObj
58+
59+
end endmarkers

0 commit comments

Comments
 (0)