Skip to content

Commit

Permalink
Eliminated separate RangePositions trait.
Browse files Browse the repository at this point in the history
One of those eternal headaches which probably sounded better
on paper. Even before range positions are the default, there's
no reason we can't have the range position code available
in all globals enabled via settings, just like everything else.
  • Loading branch information
paulp committed Mar 4, 2013
1 parent dc1cd96 commit 6898c9f
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 311 deletions.
8 changes: 2 additions & 6 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path } import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter } import reporters.{ Reporter, ConsoleReporter }
import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString, stackTraceHeadString } import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString, stackTraceHeadString }
import scala.reflect.internal.RangePositions
import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat }
import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers } import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers }
Expand Down Expand Up @@ -43,6 +42,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// the mirror -------------------------------------------------- // the mirror --------------------------------------------------


override def isCompilerUniverse = true override def isCompilerUniverse = true
override val useOffsetPositions = !currentSettings.Yrangepos.value


class GlobalMirror extends Roots(NoSymbol) { class GlobalMirror extends Roots(NoSymbol) {
val universe: self.type = self val universe: self.type = self
Expand Down Expand Up @@ -1695,10 +1695,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def createJavadoc = false def createJavadoc = false
} }


class RangePositionGlobal(settings0: Settings, reporter0: Reporter) extends Global(settings0, reporter0) with RangePositions

object Global { object Global {
def apply(settings: Settings, reporter: Reporter): Global = def apply(settings: Settings, reporter: Reporter): Global = new Global(settings, reporter)
if (settings.Yrangepos.value) new RangePositionGlobal(settings, reporter)
else new Global(settings, reporter)
} }
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/doc/DocParser.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import DocParser.Parsed
* right after parsing so it can read `DocDefs` from source code which would * right after parsing so it can read `DocDefs` from source code which would
* otherwise cause the compiler to go haywire. * otherwise cause the compiler to go haywire.
*/ */
class DocParser(settings: nsc.Settings, reporter: Reporter) extends RangePositionGlobal(settings, reporter) { class DocParser(settings: nsc.Settings, reporter: Reporter) extends Global(settings, reporter) {
def this(settings: Settings) = this(settings, new ConsoleReporter(settings)) def this(settings: Settings) = this(settings, new ConsoleReporter(settings))
def this() = this(new Settings(Console println _)) def this() = this(new Settings(Console println _))


Expand Down
4 changes: 3 additions & 1 deletion src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ trait ScaladocAnalyzer extends Analyzer {
} }
} }


class ScaladocGlobal(settings: doc.Settings, reporter: Reporter) extends Global(settings, reporter) with interactive.RangePositions { class ScaladocGlobal(settings: doc.Settings, reporter: Reporter) extends {
override val useOffsetPositions = false
} with Global(settings, reporter) {
override protected def computeInternalPhases() { override protected def computeInternalPhases() {
phasesSet += syntaxAnalyzer phasesSet += syntaxAnalyzer
phasesSet += analyzer.namerFactory phasesSet += analyzer.namerFactory
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/interactive/Global.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import mutable.{LinkedHashMap, SynchronizedMap, HashSet, SynchronizedSet}
import scala.util.control.ControlThrowable import scala.util.control.ControlThrowable
import scala.tools.nsc.io.{ AbstractFile, LogReplay, Logger, NullLogger, Replayer } import scala.tools.nsc.io.{ AbstractFile, LogReplay, Logger, NullLogger, Replayer }
import scala.tools.nsc.util.MultiHashMap import scala.tools.nsc.util.MultiHashMap
import scala.reflect.internal.util.{ SourceFile, BatchSourceFile, Position, RangePosition, NoPosition } import scala.reflect.internal.util.{ SourceFile, BatchSourceFile, Position, NoPosition }
import scala.tools.nsc.reporters._ import scala.tools.nsc.reporters._
import scala.tools.nsc.symtab._ import scala.tools.nsc.symtab._
import scala.tools.nsc.typechecker.DivergentImplicit import scala.tools.nsc.typechecker.DivergentImplicit
Expand All @@ -26,9 +26,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
* execution of the super constructor. * execution of the super constructor.
*/ */
private var initializing = true private var initializing = true
} with RangePositionGlobal(settings, _reporter) override val useOffsetPositions = false
} with scala.tools.nsc.Global(settings, _reporter)
with CompilerControl with CompilerControl
with RangePositions
with ContextTrees with ContextTrees
with RichCompilationUnits with RichCompilationUnits
with ScratchPadMaker with ScratchPadMaker
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/interactive/RangePositions.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
package scala.tools.nsc package scala.tools.nsc
package interactive package interactive


@deprecated("Use scala.reflect.internal.RangePositions", "2.11.0") @deprecated("Use scala.reflect.internal.Positions", "2.11.0")
trait RangePositions extends scala.reflect.internal.RangePositions with ast.Trees with ast.Positions { trait RangePositions extends scala.reflect.internal.Positions with ast.Trees with ast.Positions {
self: scala.tools.nsc.Global => self: scala.tools.nsc.Global =>


} }
5 changes: 1 addition & 4 deletions src/compiler/scala/tools/nsc/interpreter/IMain.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -242,10 +242,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = { protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = {
settings.outputDirs setSingleOutput replOutput.dir settings.outputDirs setSingleOutput replOutput.dir
settings.exposeEmptyPackage.value = true settings.exposeEmptyPackage.value = true
if (settings.Yrangepos.value) new Global(settings, reporter) with ReplGlobal { override def toString: String = "<global>" }
new RangePositionGlobal(settings, reporter) with ReplGlobal { override def toString: String = "<global>" }
else
new Global(settings, reporter) with ReplGlobal { override def toString: String = "<global>" }
} }


/** Parent classloader. Overridable. */ /** Parent classloader. Overridable. */
Expand Down
266 changes: 258 additions & 8 deletions src/reflect/scala/reflect/internal/Positions.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,38 +2,272 @@ package scala.reflect
package internal package internal


import util._ import util._
import scala.collection.mutable.ListBuffer


/** Handling range positions
* atPos, the main method in this trait, will add positions to a tree,
* and will ensure the following properties:
*
* 1. All nodes between the root of the tree and nodes that already have positions
* will be assigned positions.
* 2. No node which already has a position will be assigned a different range; however
* a RangePosition might become a TransparentPosition.
* 3. The position of each assigned node includes the positions of each of its children.
* 4. The positions of all solid descendants of children of an assigned node
* are mutually non-overlapping.
*
* Here, the solid descendant of a node are:
*
* If the node has a TransparentPosition, the solid descendants of all its children
* Otherwise, the singleton consisting of the node itself.
*/
trait Positions extends api.Positions { self: SymbolTable => trait Positions extends api.Positions { self: SymbolTable =>


type Position = scala.reflect.internal.util.Position type Position = scala.reflect.internal.util.Position
val NoPosition = scala.reflect.internal.util.NoPosition val NoPosition = scala.reflect.internal.util.NoPosition
implicit val PositionTag = ClassTag[Position](classOf[Position]) implicit val PositionTag = ClassTag[Position](classOf[Position])


def inform(msg: String): Unit

def useOffsetPositions: Boolean = true

/** A position that wraps a set of trees. /** A position that wraps a set of trees.
* The point of the wrapping position is the point of the default position. * The point of the wrapping position is the point of the default position.
* If some of the trees are ranges, returns a range position enclosing all ranges * If some of the trees are ranges, returns a range position enclosing all ranges
* Otherwise returns default position that is either focused or not. * Otherwise returns default position that is either focused or not.
*/ */
def wrappingPos(default: Position, trees: List[Tree]): Position = wrappingPos(default, trees, true) def wrappingPos(default: Position, trees: List[Tree]): Position = wrappingPos(default, trees, true)
def wrappingPos(default: Position, trees: List[Tree], focus: Boolean): Position = default def wrappingPos(default: Position, trees: List[Tree], focus: Boolean): Position = {
if (useOffsetPositions) default else {
val ranged = trees filter (_.pos.isRange)
if (ranged.isEmpty) if (focus) default.focus else default
else new RangePosition(default.source, (ranged map (_.pos.start)).min, default.point, (ranged map (_.pos.end)).max)
}
}


/** A position that wraps the non-empty set of trees. /** A position that wraps the non-empty set of trees.
* The point of the wrapping position is the point of the first trees' position. * The point of the wrapping position is the point of the first trees' position.
* If some of the trees are non-synthetic, returns a range position enclosing the non-synthetic trees * If some of the trees are non-synthetic, returns a range position enclosing the non-synthetic trees
* Otherwise returns a synthetic offset position to point. * Otherwise returns a synthetic offset position to point.
*/ */
def wrappingPos(trees: List[Tree]): Position = trees.head.pos def wrappingPos(trees: List[Tree]): Position = {
val headpos = trees.head.pos
if (useOffsetPositions || !headpos.isDefined) headpos
else wrappingPos(headpos, trees)
}


/** Ensure that given tree has no positions that overlap with /** Ensure that given tree has no positions that overlap with
* any of the positions of `others`. This is done by * any of the positions of `others`. This is done by
* shortening the range, assigning TransparentPositions * shortening the range, assigning TransparentPositions
* to some of the nodes in `tree` or focusing on the position. * to some of the nodes in `tree` or focusing on the position.
*/ */
def ensureNonOverlapping(tree: Tree, others: List[Tree]){ ensureNonOverlapping(tree, others, true) } def ensureNonOverlapping(tree: Tree, others: List[Tree]){ ensureNonOverlapping(tree, others, true) }
def ensureNonOverlapping(tree: Tree, others: List[Tree], focus: Boolean) {} def ensureNonOverlapping(tree: Tree, others: List[Tree], focus: Boolean) {
if (useOffsetPositions) return

def isOverlapping(pos: Position) =
pos.isRange && (others exists (pos overlaps _.pos))

if (isOverlapping(tree.pos)) {
val children = tree.children
children foreach (ensureNonOverlapping(_, others, focus))
if (tree.pos.isOpaqueRange) {
val wpos = wrappingPos(tree.pos, children, focus)
tree setPos (if (isOverlapping(wpos)) tree.pos.makeTransparent else wpos)
}
}
}

def rangePos(source: SourceFile, start: Int, point: Int, end: Int): Position =
if (useOffsetPositions) new OffsetPosition(source, point)
else new RangePosition(source, start, point, end)

def validatePositions(tree: Tree) {
if (useOffsetPositions) return

def reportTree(prefix : String, tree : Tree) {
val source = if (tree.pos.isDefined) tree.pos.source else ""
inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source)
inform("")
inform(treeStatus(tree))
inform("")
}

def positionError(msg: String)(body : => Unit) {
inform("======= Position error\n" + msg)
body
inform("\nWhile validating #" + tree.id)
inform(treeStatus(tree))
inform("\nChildren:")
tree.children map (t => " " + treeStatus(t, tree)) foreach inform
inform("=======")
throw new ValidateException(msg)
}

def validate(tree: Tree, encltree: Tree): Unit = {

if (!tree.isEmpty && tree.canHaveAttrs) {
if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value))
println("[%10s] %s".format("validate", treeStatus(tree, encltree)))

if (!tree.pos.isDefined)
positionError("Unpositioned tree #"+tree.id) {
inform("%15s %s".format("unpositioned", treeStatus(tree, encltree)))
inform("%15s %s".format("enclosing", treeStatus(encltree)))
encltree.children foreach (t => inform("%15s %s".format("sibling", treeStatus(t, encltree))))
}
if (tree.pos.isRange) {
if (!encltree.pos.isRange)
positionError("Synthetic tree ["+encltree.id+"] contains nonsynthetic tree ["+tree.id+"]") {
reportTree("Enclosing", encltree)
reportTree("Enclosed", tree)
}
if (!(encltree.pos includes tree.pos))
positionError("Enclosing tree ["+encltree.id+"] does not include tree ["+tree.id+"]") {
reportTree("Enclosing", encltree)
reportTree("Enclosed", tree)
}

findOverlapping(tree.children flatMap solidDescendants) match {
case List() => ;
case xs => {
positionError("Overlapping trees "+xs.map { case (x, y) => (x.id, y.id) }.mkString("", ", ", "")) {
reportTree("Ancestor", tree)
for((x, y) <- xs) {
reportTree("First overlapping", x)
reportTree("Second overlapping", y)
}
}
}
}
}
for (ct <- tree.children flatMap solidDescendants) validate(ct, tree)
}
}

if (!isPastTyper)
validate(tree, tree)
}

def solidDescendants(tree: Tree): List[Tree] =
if (tree.pos.isTransparent) tree.children flatMap solidDescendants
else List(tree)

/** A free range from `lo` to `hi` */
private def free(lo: Int, hi: Int): Range =
Range(new RangePosition(null, lo, lo, hi), EmptyTree)

/** The maximal free range */
private lazy val maxFree: Range = free(0, Int.MaxValue)

/** A singleton list of a non-empty range from `lo` to `hi`, or else the empty List */
private def maybeFree(lo: Int, hi: Int) =
if (lo < hi) List(free(lo, hi))
else List()

/** Insert `pos` into ranges `rs` if possible;
* otherwise add conflicting trees to `conflicting`.
*/
private def insert(rs: List[Range], t: Tree, conflicting: ListBuffer[Tree]): List[Range] = rs match {
case List() =>
assert(conflicting.nonEmpty)
rs
case r :: rs1 =>
assert(!t.pos.isTransparent)
if (r.isFree && (r.pos includes t.pos)) {
// println("subdividing "+r+"/"+t.pos)
maybeFree(t.pos.end, r.pos.end) ::: List(Range(t.pos, t)) ::: maybeFree(r.pos.start, t.pos.start) ::: rs1
} else {
if (!r.isFree && (r.pos overlaps t.pos)) conflicting += r.tree
r :: insert(rs1, t, conflicting)
}
}

/** Replace elem `t` of `ts` by `replacement` list. */
private def replace(ts: List[Tree], t: Tree, replacement: List[Tree]): List[Tree] =
if (ts.head == t) replacement ::: ts.tail
else ts.head :: replace(ts.tail, t, replacement)

/** Does given list of trees have mutually non-overlapping positions?
* pre: None of the trees is transparent
*/
def findOverlapping(cts: List[Tree]): List[(Tree, Tree)] = {
var ranges = List(maxFree)
for (ct <- cts) {
if (ct.pos.isOpaqueRange) {
val conflicting = new ListBuffer[Tree]
ranges = insert(ranges, ct, conflicting)
if (conflicting.nonEmpty) return conflicting.toList map (t => (t, ct))
}
}
List()
}

/** Set position of all children of a node
* @param pos A target position.
* Uses the point of the position as the point of all positions it assigns.
* Uses the start of this position as an Offset position for unpositioed trees
* without children.
* @param trees The children to position. All children must be positionable.
*/
private def setChildrenPos(pos: Position, trees: List[Tree]): Unit = try {
for (tree <- trees) {
if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) {
val children = tree.children
if (children.isEmpty) {
tree setPos pos.focus
} else {
setChildrenPos(pos, children)
tree setPos wrappingPos(pos, children)
}
}
}
} catch {
case ex: Exception =>
println("error while set children pos "+pos+" of "+trees)
throw ex
}


class ValidateException(msg : String) extends Exception(msg)


def rangePos(source: SourceFile, start: Int, point: Int, end: Int): Position = new OffsetPosition(source, point)
def validatePositions(tree: Tree) {} /** A locator for trees with given positions.
* Given a position `pos`, locator.apply returns
* the smallest tree that encloses `pos`.
*/
class Locator(pos: Position) extends Traverser {
var last: Tree = _
def locateIn(root: Tree): Tree = {
this.last = EmptyTree
traverse(root)
this.last
}
protected def isEligible(t: Tree) = !t.pos.isTransparent
override def traverse(t: Tree) {
t match {
case tt : TypeTree if tt.original != null && (tt.pos includes tt.original.pos) =>
traverse(tt.original)
case _ =>
if (t.pos includes pos) {
if (isEligible(t)) last = t
super.traverse(t)
} else t match {
case mdef: MemberDef =>
traverseTrees(mdef.mods.annotations)
case _ =>
}
}
}
}

case class Range(pos: Position, tree: Tree) {
def isFree = tree == EmptyTree
}

class TypedLocator(pos: Position) extends Locator(pos) {
override protected def isEligible(t: Tree) = super.isEligible(t) && t.tpe != null
}


trait PosAssigner extends Traverser { trait PosAssigner extends Traverser {
var pos: Position var pos: Position
Expand Down Expand Up @@ -62,9 +296,25 @@ trait Positions extends api.Positions { self: SymbolTable =>
} }
} }


/** Position a tree.
* This means: Set position of a node and position all its unpositioned children.
*/
def atPos[T <: Tree](pos: Position)(tree: T): T = { def atPos[T <: Tree](pos: Position)(tree: T): T = {
posAssigner.pos = pos if (useOffsetPositions || !pos.isOpaqueRange) {
posAssigner.traverse(tree) posAssigner.pos = pos
tree posAssigner.traverse(tree)
tree
}
else {
if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) {
tree.setPos(pos)
val children = tree.children
if (children.nonEmpty) {
if (children.tail.isEmpty) atPos(pos)(children.head)
else setChildrenPos(pos, children)
}
}
tree
}
} }
} }
Loading

0 comments on commit 6898c9f

Please sign in to comment.