Skip to content

Commit

Permalink
Merge pull request scala#10040 from lrytz/t12597
Browse files Browse the repository at this point in the history
Fix AST positions for XML literals, selections from blocks
  • Loading branch information
lrytz authored Jun 8, 2022
2 parents bac51a6 + ee1bf4c commit 35c6f5d
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 260 deletions.
11 changes: 5 additions & 6 deletions src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ trait MarkupParsers {
xTakeUntil(handle.charData, () => r2p(start, mid, curOffset), "]]>")
}

def xUnparsed: Tree = {
val start = curOffset
def xUnparsed(start: Int): Tree = {
xTakeUntil(handle.unparsed, () => r2p(start, start, curOffset), "</xml:unparsed>")
}

Expand Down Expand Up @@ -304,7 +303,7 @@ trait MarkupParsers {
* | xmlTag1 '/' '>'
*/
def element: Tree = {
val start = curOffset
val start = curOffset - 1 // include <
val (qname, attrMap) = xTag(())
if (ch == '/') { // empty element
xToken("/>")
Expand All @@ -313,7 +312,7 @@ trait MarkupParsers {
else { // handle content
xToken('>')
if (qname == "xml:unparsed")
return xUnparsed
return xUnparsed(start)

debugLastStartElement.push((start, qname))
val ts = content
Expand Down Expand Up @@ -381,7 +380,7 @@ trait MarkupParsers {
handle.isPattern = false

val ts = new ArrayBuffer[Tree]
val start = curOffset
val start = curOffset - 1 // include <
content_LT(ts)

// parse more XML?
Expand Down Expand Up @@ -445,7 +444,7 @@ trait MarkupParsers {
* | Name [S] '/' '>'
*/
def xPattern: Tree = {
val start = curOffset
val start = curOffset - 1 // include <
val qname = xName
debugLastStartElement.push((start, qname))
xSpaceOpt()
Expand Down
49 changes: 28 additions & 21 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1218,12 +1218,12 @@ self =>

def identOrMacro(): Name = if (isMacro) rawIdent() else ident()

def selector(t0: Tree): Tree = {
def selector(start: Offset, t0: Tree): Tree = {
val t = stripParens(t0)
val point = if (isIdent) in.offset else in.lastOffset //scala/bug#8459
//assert(t.pos.isDefined, t)
if (t != EmptyTree)
Select(t, ident(skipIt = false)) setPos r2p(t0.pos.start, point, in.lastOffset)
Select(t, ident(skipIt = false)) setPos r2p(start, point, in.lastOffset)
else
errorTermTree // has already been reported
}
Expand All @@ -1241,14 +1241,14 @@ self =>
in.nextToken()
t = atPos(start) { This(tpnme.EMPTY) }
if (!thisOK || in.token == DOT) {
t = selectors(t, typeOK, accept(DOT))
t = selectors(start, t, typeOK, accept(DOT))
}
} else if (in.token == SUPER) {
in.nextToken()
t = atPos(start) { Super(This(tpnme.EMPTY), mixinQualifierOpt()) }
accept(DOT)
t = selector(t)
if (in.token == DOT) t = selectors(t, typeOK, in.skipToken())
t = selector(start, t)
if (in.token == DOT) t = selectors(start, t, typeOK, in.skipToken())
} else {
val tok = in.token
val name = ident()
Expand All @@ -1262,29 +1262,29 @@ self =>
in.nextToken()
t = atPos(start) { This(name.toTypeName) }
if (!thisOK || in.token == DOT)
t = selectors(t, typeOK, accept(DOT))
t = selectors(start, t, typeOK, accept(DOT))
} else if (in.token == SUPER) {
in.nextToken()
t = atPos(start) { Super(This(name.toTypeName), mixinQualifierOpt()) }
accept(DOT)
t = selector(t)
if (in.token == DOT) t = selectors(t, typeOK, in.skipToken())
t = selector(start, t)
if (in.token == DOT) t = selectors(start, t, typeOK, in.skipToken())
} else {
t = selectors(t, typeOK, dotOffset)
t = selectors(start, t, typeOK, dotOffset)
}
}
}
t
}

def selectors(t: Tree, typeOK: Boolean, dotOffset: Offset): Tree =
def selectors(start: Offset, t: Tree, typeOK: Boolean, dotOffset: Offset): Tree =
if (typeOK && in.token == TYPE) {
in.nextToken()
atPos(t.pos.start, dotOffset) { SingletonTypeTree(t) }
}
else {
val t1 = selector(t)
if (in.token == DOT) { selectors(t1, typeOK, in.skipToken()) }
val t1 = selector(start, t)
if (in.token == DOT) { selectors(start, t1, typeOK, in.skipToken()) }
else t1
}

Expand Down Expand Up @@ -1312,7 +1312,7 @@ self =>
def qualId(): Tree = {
val start = in.offset
val id = atPos(start) { Ident(ident()) }
if (in.token == DOT) { selectors(id, typeOK = false, in.skipToken()) }
if (in.token == DOT) { selectors(start, id, typeOK = false, in.skipToken()) }
else id
}
/** Calls `qualId()` and manages some package state. */
Expand Down Expand Up @@ -1730,13 +1730,14 @@ self =>
*/
def prefixExpr(): Tree = {
if (isUnaryOp) {
atPos(in.offset) {
val start = in.offset
atPos(start) {
if (lookingAhead(isSimpleExprIntro)) {
val namePos = in.offset
val uname = nme.toUnaryName(rawIdent().toTermName)
if (uname == nme.UNARY_- && isNumericLit)
/* start at the -, not the number */
simpleExprRest(literal(isNegated = true, start = namePos), canApply = true)
simpleExprRest(start, literal(isNegated = true, start = namePos), canApply = true)
else
Select(stripParens(simpleExpr()), uname)
}
Expand All @@ -1762,6 +1763,7 @@ self =>
*/
def simpleExpr(): Tree = {
var canApply = true
val start = in.offset
val t =
if (isLiteral) literal()
else in.token match {
Expand All @@ -1787,15 +1789,15 @@ self =>
case _ =>
syntaxErrorOrIncompleteAnd("illegal start of simple expression", skipIt = true)(errorTermTree)
}
simpleExprRest(t, canApply = canApply)
simpleExprRest(start, t, canApply = canApply)
}

def simpleExprRest(t: Tree, canApply: Boolean): Tree = {
def simpleExprRest(start: Offset, t: Tree, canApply: Boolean): Tree = {
if (canApply) newLineOptWhenFollowedBy(LBRACE)
in.token match {
case DOT =>
in.nextToken()
simpleExprRest(selector(t), canApply = true)
simpleExprRest(start, selector(start, t), canApply = true)
case LBRACKET =>
val t1 = stripParens(t)
t1 match {
Expand All @@ -1804,7 +1806,7 @@ self =>
while (in.token == LBRACKET)
app = atPos(t.pos.start, in.offset)(TypeApply(app, exprTypeArgs()))

simpleExprRest(app, canApply = true)
simpleExprRest(start, app, canApply = true)
case _ =>
t1
}
Expand All @@ -1820,7 +1822,7 @@ self =>
}
Apply(sel, argumentExprs())
}
simpleExprRest(app, canApply = true)
simpleExprRest(start, app, canApply = true)
case USCORE =>
atPos(t.pos.start, in.skipToken()) { MethodValue(stripParens(t)) }
case _ =>
Expand Down Expand Up @@ -1854,9 +1856,14 @@ self =>
* }}}
*/
def blockExpr(): Tree = atPos(in.offset) {
val start = in.offset
inBraces {
if (in.token == CASE) Match(EmptyTree, caseClauses())
else block()
} match {
case b: Block if b.pos == NoPosition =>
b.setPos(r2p(start, start, in.lastOffset))
case t => t
}
}

Expand Down Expand Up @@ -2564,7 +2571,7 @@ self =>
in.nextToken()
val t = atPos(start)(This(name))
accept(DOT)
val result = selector(t)
val result = selector(start, t)
accept(DOT)
result
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
val buffer = ValDef(NoMods, _buf, TypeTree(), New(_scala_xml_NodeBuffer, ListOfNil))
val applies = args filterNot isEmptyText map (t => Apply(Select(Ident(_buf), _plus), List(t)))

atPos(pos)( Block(buffer :: applies.toList, Ident(_buf)) )
atPos(pos)( gen.mkBlock(buffer :: applies.toList ::: List(Ident(_buf))) )
}

/** Returns (Some(prefix) | None, rest) based on position of ':' */
Expand Down Expand Up @@ -272,6 +272,6 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
args
)

atPos(pos.makeTransparent)( Block(nsResult, Block(attrResult, body)) )
atPos(pos.makeTransparent)( gen.mkBlock(nsResult :+ gen.mkBlock(attrResult :+ body)) )
}
}
2 changes: 1 addition & 1 deletion test/files/neg/t2275a.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ t2275a.scala:4: error: in XML literal: in XML content, please use '}}' to expres
^
t2275a.scala:3: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <br>
<br>
^
^
t2275a.scala:4: error: ';' expected but 'else' found.
}else{
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t2275b.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ t2275b.scala:2: error: in XML literal: in XML content, please use '}}' to expres
^
t2275b.scala:2: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <br>
{<br>}xx
^
^
t2275b.scala:3: error: '}' expected but eof found.
}
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t3604.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ t3604.scala:3: error: in XML literal: expected closing tag of abbr
^
t3604.scala:3: error: start tag was here: abbr>
<abbr></div>
^
^
two errors found
2 changes: 1 addition & 1 deletion test/files/neg/t3769.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ t3769.scala:2: error: in XML literal: expected closing tag of a
^
t3769.scala:2: error: start tag was here: a>
val x = <b> <c><a></c> {"text"} </b>
^
^
two errors found
2 changes: 1 addition & 1 deletion test/files/neg/t4069.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ t4069.scala:9: error: in XML literal: in XML content, please use '}}' to express
^
t4069.scala:4: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <div>
<div>
^
^
t4069.scala:10: error: '}' expected but eof found.
}
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t5702-neg-ugly-xbrace.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ t5702-neg-ugly-xbrace.scala:13: error: in XML literal: in XML content, please us
^
t5702-neg-ugly-xbrace.scala:11: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <top>
val <top>{a, z@_*)</top> = xml
^
^
t5702-neg-ugly-xbrace.scala:14: error: illegal start of simple pattern
}
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/xmltruncated7.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ xmltruncated7.scala:2: error: in XML literal: in XML content, please use '}}' to
^
xmltruncated7.scala:2: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <p>
<p>foo}: </p>
^
^
two errors found
10 changes: 10 additions & 0 deletions test/files/run/t12597.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
List((26,30))
List((26,33))
List((26,42), (26,33))
List((26,44), (27,34))
List((26,46), (28,35))
List((26,37), (26,26), (26,33), (33,37))
List((26,46), (26,37), (26,26), (26,33), (33,37))
List((26,36), (26,27))
List((26,36), (26,27))
List((26,51), (26,42), (28,37), (36,37), (39,40))
31 changes: 31 additions & 0 deletions test/files/run/t12597.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import scala.tools.partest._
import scala.collection.mutable.LinkedHashMap

object Test extends CompilerTest {
import global._
override def extraSettings = super.extraSettings + " -Yrangepos -Ystop-after:parser"
val tests = List(
"class A1 { def t = <a/> }",
"class A2 { def t = <a></a> }",
"class A3 { def t = <a></a>.toString }",
"class A4 { def t = (<a></a>).toString }",
"class A5 { def t = { <a></a> }.toString }",
"class A6 { def t = <a></a><b/> }",
"class A7 { def t = <a></a><b/>.toString }",
"class B1 { def t(c: A1) = c.toString }",
"class B2 { def t(c: A1) = c toString }",
"class B3 { def t(c: A1) = { val x = c; x }.toString }",
// ^ 26 ^ 36
)

override def sources = tests

def check(source: String, unit: CompilationUnit): Unit = unit.body foreach {
case dd: DefDef if dd.name.startsWith("t") =>
val poss = dd.rhs.collect {
case t if t.pos != NoPosition => (t.pos.start, t.pos.end)
}.distinct
println(poss)
case _ =>
}
}
Loading

0 comments on commit 35c6f5d

Please sign in to comment.