From d839dde86af996d8477df50ddba85d1fb3868983 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Jun 2021 14:32:27 +0200 Subject: [PATCH] modify spans of statements with end markers --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 41 +++++++++++-------- .../dotty/tools/dotc/parsing/Parsers.scala | 12 ++++-- .../dotc/semanticdb/ExtractSemanticDB.scala | 2 +- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5d953140ce78..c62e88cd3e84 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -875,7 +875,6 @@ object desugar { val modul = ValDef(moduleName, clsRef, New(clsRef, Nil)) .withMods(mods.toTermFlags & RetainedModuleValFlags | ModuleValCreationFlags) .withSpan(mdef.span.startPos) - .withEndIndex(copyFrom = mdef) // copy over the end marker position to the module val val ValDef(selfName, selfTpt, _) = impl.self val selfMods = impl.self.mods if (!selfTpt.isEmpty) report.error(ObjectMayNotHaveSelfType(mdef), impl.self.srcPos) @@ -885,6 +884,7 @@ object desugar { val clsTmpl = cpy.Template(impl)(self = clsSelf, body = impl.body) val cls = TypeDef(clsName, clsTmpl) .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags) + .withEndIndex(copyFrom = mdef) // copy over the end marker position to the module class def Thicket(modul, classDef(cls).withSpan(mdef.span)) } } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 6a0146241788..64102196b3d4 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -327,42 +327,45 @@ object Trees { extension (mdef: untpd.DefTree) def mods: untpd.Modifiers = mdef.rawMods - /** PackageDef | NamedDefTree */ - sealed trait WithEndMarker: - self: Attachment.Container => + sealed trait WithEndMarker[-T >: Untyped]: + self: PackageDef[T] | NamedDefTree[T] => import WithEndMarker.* final def endSpan(using Context): Span = - self.getAttachment(EndIndex) match - case Some(end) => - val realName = srcName.stripModuleClassSuffix.lastPart - Span(end - realName.length, end) - case none => NoSpan + if hasEnd then + val realName = srcName.stripModuleClassSuffix.lastPart + span.withStart(span.end - realName.length) + else + NoSpan protected def srcName(using Context): Name - final def withEndIndex(index: Int): self.type = - self.withAttachment(EndIndex, index) + final def withEndIndex(): self.type = + self.withAttachment(EndIndex, ()) - final def withEndIndex(copyFrom: WithEndMarker): self.type = - copyFrom.endIndex.foreach(withEndIndex) - this + final def withEndIndex(copyFrom: WithEndMarker[T @uncheckedVariance]): self.type = + if copyFrom.hasEnd then + this.withEndIndex() + else + this final def dropEndIndex(): self.type = self.removeAttachment(EndIndex) this - protected def endIndex: Option[Int] = self.getAttachment(EndIndex) + protected def hasEnd: Boolean = self.hasAttachment(EndIndex) object WithEndMarker: - /** Property key for trees with an `end` marker */ - private val EndIndex: Property.StickyKey[Int] = Property.StickyKey() + /** Property key that signals the tree was terminated + * with an `end` marker in the source code + */ + private val EndIndex: Property.StickyKey[Unit] = Property.StickyKey() end WithEndMarker abstract class NamedDefTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) - extends NameTree[T] with DefTree[T] with WithEndMarker { + extends NameTree[T] with DefTree[T] with WithEndMarker[T] { type ThisTree[-T >: Untyped] <: NamedDefTree[T] protected def srcName(using Context): Name = @@ -897,7 +900,7 @@ 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] with WithEndMarker { + extends ProxyTree[T] with WithEndMarker[T] { type ThisTree[-T >: Untyped] = PackageDef[T] def forwardTo: RefTree[T] = pid protected def srcName(using Context): Name = pid.name @@ -1092,6 +1095,8 @@ object Trees { type Annotated = Trees.Annotated[T] type Thicket = Trees.Thicket[T] + type WithEndMarker = Trees.WithEndMarker[T] + @sharable val EmptyTree: Thicket = genericEmptyTree @sharable val EmptyValDef: ValDef = genericEmptyValDef @sharable val ContextualEmptyTree: Thicket = new EmptyTree() // an empty tree marking a contextual closure diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 887621468ab7..ecebf18399d7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1275,7 +1275,7 @@ object Parsers { def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit = - def matches(stat: Tree): Boolean = stat match + def matches(stat: T): Boolean = stat match case stat: MemberDef if !stat.name.isEmpty => if stat.name == nme.CONSTRUCTOR then in.token == THIS else in.isIdent && in.name == stat.name.toTermName @@ -1293,14 +1293,20 @@ object Parsers { case _: (ForYield | ForDo) => in.token == FOR case _ => false - def matchesAndSetEnd(stat: Tree): Boolean = { + def matchesAndSetEnd(stat: T): Boolean = { val didMatch = matches(stat) if didMatch then stat match case stat: WithEndMarker => - stat.withEndIndex(index = in.lastCharOffset) + stat.withEndIndex() case _ => () + + // VERY SNEAKY MUTATION HERE: + // `withSpan` can clone the tree, so we must replace the last statment + stats.dropRightInPlace(1) + stats.addOne(stat.withSpan(stat.span.withEnd(in.lastCharOffset))) + end if didMatch } diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index b97e1f3f8484..b233488b9a8c 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -240,7 +240,7 @@ class ExtractSemanticDB extends Phase: traverseChildren(tree) tree match - case tree: WithEndMarker => + case tree: WithEndMarker[t] => val endSpan = tree.endSpan if endSpan.exists then registerUseGuarded(None, tree.symbol, endSpan, tree.source)