Skip to content

Commit

Permalink
record end markers in trees and semanticdb
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed May 20, 2021
1 parent d2dd083 commit ce30677
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 6 deletions.
36 changes: 34 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,40 @@ object Trees {

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

abstract class NamedDefTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] {
/** PackageDef | NamedDefTree */
sealed trait WithEndMarker:
self: Attachment.Container =>

import WithEndMarker.*

final def endToken: SimpleName = endMarker.stripModuleClassSuffix.lastPart

protected def endMarker: Name

def setEndSpan(span: Span): self.type =
self.putAttachment(EndMarker, (span, endToken))
this

def endSpan: Option[Span] = self.getAttachment(EndMarker).collect {
// var setter copies the end marker, but the tree will have a different name,
// so check that the end marker has not been renamed
case (span, token) if token eq endToken => span
}

object WithEndMarker:
/** Property key for trees with an `end` marker */
private val EndMarker: Property.StickyKey[(Span, SimpleName)] = Property.StickyKey()

end WithEndMarker

abstract class NamedDefTree[-T >: Untyped](implicit @constructorOnly src: SourceFile)
extends NameTree[T] with DefTree[T] with WithEndMarker {
type ThisTree[-T >: Untyped] <: NamedDefTree[T]

protected def endMarker =
if name == nme.CONSTRUCTOR then nme.this_
else name

/** The position of the name defined by this definition.
* This is a point position if the definition is synthetic, or a range position
* if the definition comes from source.
Expand Down Expand Up @@ -857,9 +888,10 @@ object Trees {

/** package pid { stats } */
case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] {
extends ProxyTree[T] with WithEndMarker {
type ThisTree[-T >: Untyped] = PackageDef[T]
def forwardTo: RefTree[T] = pid
protected def endMarker: Name = pid.name
}

/** arg @annot */
Expand Down
18 changes: 15 additions & 3 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1283,8 +1283,8 @@ object Parsers {
in.token == IDENTIFIER && in.name == nme.extension
case PackageDef(pid: RefTree, _) =>
in.isIdent && in.name == pid.name
case PatDef(_, IdPattern(id, _) :: Nil, _, _) =>
in.isIdent && in.name == id.name
case PatDef(_, IdPattern(id, _) :: Nil, _, _) => // TODO: it looks like this case is never reached
in.isIdent && in.name == id.name
case stat: MemberDef if stat.mods.is(Given) => in.token == GIVEN
case _: PatDef => in.token == VAL
case _: If => in.token == IF
Expand All @@ -1295,9 +1295,21 @@ object Parsers {
case _: (ForYield | ForDo) => in.token == FOR
case _ => false

def matchesWithUpdated(stat: Tree): Boolean = {
val didMatch = matches(stat)
if didMatch then
stat match
case stat: WithEndMarker =>
val end = in.lastCharOffset
stat.setEndSpan(Span(end - stat.endToken.length, end))
case _ =>
()
didMatch
}

if in.token == END then
val start = in.skipToken()
if stats.isEmpty || !matches(stats.last) then
if stats.isEmpty || !matchesWithUpdated(stats.last) then
syntaxError("misaligned end marker", Span(start, in.lastCharOffset))
in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion
in.nextToken()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import core._
import Phases._
import ast.tpd._
import ast.untpd.given
import ast.Trees.mods
import ast.Trees.{mods, WithEndMarker}
import Contexts._
import Symbols._
import Flags._
Expand Down Expand Up @@ -240,6 +240,12 @@ class ExtractSemanticDB extends Phase:
case _ =>
traverseChildren(tree)

tree match
case tree: WithEndMarker =>
for endSpan <- tree.endSpan do
registerUseGuarded(None, tree.symbol, endSpan, tree.source)
case _ =>

end traverse

private def funParamSymbol(funSym: Symbol)(using Context): Name => String =
Expand Down
53 changes: 53 additions & 0 deletions tests/semanticdb/expect/EndMarkers.expect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package endmarkers:

class MultiCtor/*<-endmarkers::MultiCtor#*/(val i/*<-endmarkers::MultiCtor#i.*/: Int/*->scala::Int#*/):
def this()/*<-endmarkers::MultiCtor#`<init>`(+1).*/ =
this(23)
end this/*->endmarkers::MultiCtor#`<init>`(+1).*/
end MultiCtor/*->endmarkers::MultiCtor#*/

/*<-endmarkers::EndMarkers$package.*/def topLevelMethod/*<-endmarkers::EndMarkers$package.topLevelMethod().*/: String/*->scala::Predef.String#*/ =
"hello"
end topLevelMethod/*->endmarkers::EndMarkers$package.topLevelMethod().*/

val topLevelVal/*<-endmarkers::EndMarkers$package.topLevelVal.*/: Int/*->scala::Int#*/ =
23
end topLevelVal/*->endmarkers::EndMarkers$package.topLevelVal.*/

var topLevelVar/*<-endmarkers::EndMarkers$package.topLevelVar().*/: String/*->scala::Predef.String#*/ =
""
end topLevelVar/*->endmarkers::EndMarkers$package.topLevelVar().*/

class Container/*<-endmarkers::Container#*/:

def foo/*<-endmarkers::Container#foo().*/ =
(/*->scala::Tuple3.apply().*/1,2,3)
end foo/*->endmarkers::Container#foo().*/

val bar/*<-endmarkers::Container#bar.*/ =
(/*->scala::Tuple3.apply().*/4,5,6)
end bar/*->endmarkers::Container#bar.*/

var baz/*<-endmarkers::Container#baz().*/ =
15
end baz/*->endmarkers::Container#baz().*/

end Container/*->endmarkers::Container#*/

def topLevelWithLocals/*<-endmarkers::EndMarkers$package.topLevelWithLocals().*/: Unit/*->scala::Unit#*/ =

val localVal/*<-local0*/ =
37
end localVal/*->local0*/

var localVar/*<-local1*/ =
43
end localVar/*->local1*/

def localDef/*<-local2*/ =
97
end localDef/*->local2*/

end topLevelWithLocals/*->endmarkers::EndMarkers$package.topLevelWithLocals().*/

end endmarkers
53 changes: 53 additions & 0 deletions tests/semanticdb/expect/EndMarkers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package endmarkers:

class MultiCtor(val i: Int):
def this() =
this(23)
end this
end MultiCtor

def topLevelMethod: String =
"hello"
end topLevelMethod

val topLevelVal: Int =
23
end topLevelVal

var topLevelVar: String =
""
end topLevelVar

class Container:

def foo =
(1,2,3)
end foo

val bar =
(4,5,6)
end bar

var baz =
15
end baz

end Container

def topLevelWithLocals: Unit =

val localVal =
37
end localVal

var localVar =
43
end localVar

def localDef =
97
end localDef

end topLevelWithLocals

end endmarkers
77 changes: 77 additions & 0 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,83 @@ Occurrences:
[0:8..0:15): example <- example/
[2:7..2:18): EmptyObject <- example/EmptyObject.

expect/EndMarkers.scala
-----------------------

Summary:
Schema => SemanticDB v4
Uri => EndMarkers.scala
Text => empty
Language => Scala
Symbols => 22 entries
Occurrences => 40 entries

Symbols:
endmarkers/Container# => class Container
endmarkers/Container#`<init>`(). => primary ctor <init>
endmarkers/Container#`baz_=`(). => var method baz_=
endmarkers/Container#`baz_=`().(x$1) => param x$1
endmarkers/Container#bar. => val method bar
endmarkers/Container#baz(). => var method baz
endmarkers/Container#foo(). => method foo
endmarkers/EndMarkers$package. => final package object endmarkers
endmarkers/EndMarkers$package.`topLevelVar_=`(). => var method topLevelVar_=
endmarkers/EndMarkers$package.`topLevelVar_=`().(x$1) => param x$1
endmarkers/EndMarkers$package.topLevelMethod(). => method topLevelMethod
endmarkers/EndMarkers$package.topLevelVal. => val method topLevelVal
endmarkers/EndMarkers$package.topLevelVar(). => var method topLevelVar
endmarkers/EndMarkers$package.topLevelWithLocals(). => method topLevelWithLocals
endmarkers/MultiCtor# => class MultiCtor
endmarkers/MultiCtor#`<init>`(). => primary ctor <init>
endmarkers/MultiCtor#`<init>`().(i) => val param i
endmarkers/MultiCtor#`<init>`(+1). => ctor <init>
endmarkers/MultiCtor#i. => val method i
local0 => val local localVal
local1 => var local localVar
local2 => local localDef

Occurrences:
[0:8..0:18): endmarkers <- endmarkers/
[2:8..2:17): MultiCtor <- endmarkers/MultiCtor#
[2:17..2:17): <- endmarkers/MultiCtor#`<init>`().
[2:22..2:23): i <- endmarkers/MultiCtor#i.
[2:25..2:28): Int -> scala/Int#
[3:8..3:14): <- endmarkers/MultiCtor#`<init>`(+1).
[4:11..4:11): -> endmarkers/MultiCtor#`<init>`().
[5:8..5:12): this -> endmarkers/MultiCtor#`<init>`(+1).
[6:6..6:15): MultiCtor -> endmarkers/MultiCtor#
[8:2..8:2): <- endmarkers/EndMarkers$package.
[8:6..8:20): topLevelMethod <- endmarkers/EndMarkers$package.topLevelMethod().
[8:22..8:28): String -> scala/Predef.String#
[10:6..10:20): topLevelMethod -> endmarkers/EndMarkers$package.topLevelMethod().
[12:6..12:17): topLevelVal <- endmarkers/EndMarkers$package.topLevelVal.
[12:19..12:22): Int -> scala/Int#
[14:6..14:17): topLevelVal -> endmarkers/EndMarkers$package.topLevelVal.
[16:6..16:17): topLevelVar <- endmarkers/EndMarkers$package.topLevelVar().
[16:19..16:25): String -> scala/Predef.String#
[18:6..18:17): topLevelVar -> endmarkers/EndMarkers$package.topLevelVar().
[20:8..20:17): Container <- endmarkers/Container#
[22:4..22:4): <- endmarkers/Container#`<init>`().
[22:8..22:11): foo <- endmarkers/Container#foo().
[23:7..23:7): -> scala/Tuple3.apply().
[24:8..24:11): foo -> endmarkers/Container#foo().
[26:8..26:11): bar <- endmarkers/Container#bar.
[27:7..27:7): -> scala/Tuple3.apply().
[28:8..28:11): bar -> endmarkers/Container#bar.
[30:8..30:11): baz <- endmarkers/Container#baz().
[32:8..32:11): baz -> endmarkers/Container#baz().
[34:6..34:15): Container -> endmarkers/Container#
[36:6..36:24): topLevelWithLocals <- endmarkers/EndMarkers$package.topLevelWithLocals().
[36:26..36:30): Unit -> scala/Unit#
[38:8..38:16): localVal <- local0
[40:8..40:16): localVal -> local0
[42:8..42:16): localVar <- local1
[44:8..44:16): localVar -> local1
[46:8..46:16): localDef <- local2
[48:8..48:16): localDef -> local2
[50:6..50:24): topLevelWithLocals -> endmarkers/EndMarkers$package.topLevelWithLocals().
[52:4..52:14): endmarkers -> endmarkers/

expect/EnumVal.scala
--------------------

Expand Down

0 comments on commit ce30677

Please sign in to comment.