Skip to content

Commit e21dce5

Browse files
committed
Fix quote parsing
Correctly handle 'this, 'true, 'false, 'null.
1 parent c594dff commit e21dce5

File tree

118 files changed

+226
-206
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+226
-206
lines changed

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package parsing
44

55
import core.Contexts._
6+
import core.Names.SimpleName
67
import Scanners._
78
import util.SourceFile
89
import JavaTokens._
@@ -13,8 +14,10 @@ object JavaScanners {
1314

1415
class JavaScanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) {
1516

16-
def toToken(idx: Int): Token =
17+
def toToken(name: SimpleName): Token = {
18+
val idx = name.start
1719
if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER
20+
}
1821

1922
private class JavaTokenData0 extends TokenData
2023

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

+39-23
Original file line numberDiff line numberDiff line change
@@ -703,25 +703,50 @@ object Parsers {
703703
def qualId(): Tree = dotSelectors(termIdent())
704704

705705
/** SimpleExpr ::= literal
706-
* | symbol
706+
* | 'id | 'this | 'true | 'false | 'null
707707
* | null
708708
* @param negOffset The offset of a preceding `-' sign, if any.
709709
* If the literal is not negated, negOffset = in.offset.
710710
*/
711711
def literal(negOffset: Int = in.offset, inPattern: Boolean = false): Tree = {
712-
def finish(value: Any): Tree = {
713-
val t = atSpan(negOffset) { Literal(Constant(value)) }
714-
in.nextToken()
715-
t
712+
713+
def literalOf(token: Token): Literal = {
714+
val isNegated = negOffset < in.offset
715+
val value = token match {
716+
case CHARLIT => in.charVal
717+
case INTLIT => in.intVal(isNegated).toInt
718+
case LONGLIT => in.intVal(isNegated)
719+
case FLOATLIT => in.floatVal(isNegated).toFloat
720+
case DOUBLELIT => in.floatVal(isNegated)
721+
case STRINGLIT | STRINGPART => in.strVal
722+
case TRUE => true
723+
case FALSE => false
724+
case NULL => null
725+
case _ =>
726+
syntaxErrorOrIncomplete(IllegalLiteral())
727+
null
728+
}
729+
Literal(Constant(value))
716730
}
717-
val isNegated = negOffset < in.offset
731+
718732
atSpan(negOffset) {
719733
if (in.token == QUOTEID) {
720-
if ((staged & StageKind.Spliced) != 0 && isIdentifierStart(in.name(1)))
721-
Quote(atSpan(in.offset + 1)(Ident(in.name.drop(1))))
734+
if ((staged & StageKind.Spliced) != 0 && isIdentifierStart(in.name(0))) {
735+
val t = atSpan(in.offset + 1) {
736+
val tok = in.toToken(in.name)
737+
tok match {
738+
case TRUE | FALSE | NULL => literalOf(tok)
739+
case THIS => This(EmptyTypeIdent)
740+
case _ => Ident(in.name)
741+
}
742+
}
743+
in.nextToken()
744+
Quote(t)
745+
}
722746
else {
723747
migrationWarningOrError(em"""symbol literal '${in.name} is no longer supported,
724-
|use a string literal "${in.name}" or an application Symbol("${in.name}") instead.""")
748+
|use a string literal "${in.name}" or an application Symbol("${in.name}") instead,
749+
|or enclose in braces '{${in.name}} if you want a quoted expression.""")
725750
if (in.isScala2Mode) {
726751
patch(source, Span(in.offset, in.offset + 1), "Symbol(\"")
727752
patch(source, Span(in.charOffset - 1), "\")")
@@ -730,20 +755,11 @@ object Parsers {
730755
}
731756
}
732757
else if (in.token == INTERPOLATIONID) interpolatedString(inPattern)
733-
else finish(in.token match {
734-
case CHARLIT => in.charVal
735-
case INTLIT => in.intVal(isNegated).toInt
736-
case LONGLIT => in.intVal(isNegated)
737-
case FLOATLIT => in.floatVal(isNegated).toFloat
738-
case DOUBLELIT => in.floatVal(isNegated)
739-
case STRINGLIT | STRINGPART => in.strVal
740-
case TRUE => true
741-
case FALSE => false
742-
case NULL => null
743-
case _ =>
744-
syntaxErrorOrIncomplete(IllegalLiteral())
745-
null
746-
})
758+
else {
759+
val t = literalOf(in.token)
760+
in.nextToken()
761+
t
762+
}
747763
}
748764
}
749765

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,12 @@ object Scanners {
100100
def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = {
101101
target.name = termName(flushBuf(litBuf))
102102
target.token = idtoken
103-
if (idtoken == IDENTIFIER) {
104-
val idx = target.name.start
105-
target.token = toToken(idx)
106-
}
103+
if (idtoken == IDENTIFIER)
104+
target.token = toToken(target.name)
107105
}
108106

109-
def toToken(idx: Int): Token
107+
/** The token for given `name`. Either IDENTIFIER or a keyword. */
108+
def toToken(name: SimpleName): Token
110109

111110
/** Clear buffer and set string */
112111
def setStrVal(): Unit =
@@ -215,9 +214,11 @@ object Scanners {
215214
IDENTIFIER
216215
}
217216

218-
def toToken(idx: Int): Token =
217+
def toToken(name: SimpleName): Token = {
218+
val idx = name.start
219219
if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx))
220220
else IDENTIFIER
221+
}
221222

222223
private class TokenData0 extends TokenData
223224

docs/docs/reference/other-new-features/tasty-reflect.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ To provide reflection capabilities in macros we need to add an implicit paramete
2424
import scala.quoted._
2525
import scala.tasty._
2626

27-
inline def natConst(x: Int): Int = ~natConstImpl('{x})
27+
inline def natConst(x: Int): Int = ~natConstImpl('x)
2828

2929
def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = {
3030
import reflection._

library/src-bootstrapped/scala/StagedTuple.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ object StagedTuple {
253253

254254
def bind[T: Type](in: Expr[U] => Expr[T]): Expr[T] = '{
255255
val t: U = $expr
256-
${in('{t})}
256+
${in('t)}
257257
}
258258

259259
}

library/src-bootstrapped/scala/Tuple.scala

+10-10
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ sealed trait Tuple extends Any {
3232
}
3333

3434
inline def stagedToArray: Array[Object] =
35-
${ StagedTuple.toArrayStaged('{this}, constValueOpt[BoundedSize[this.type]]) }
35+
${ StagedTuple.toArrayStaged('this, constValueOpt[BoundedSize[this.type]]) }
3636

3737
inline def *: [H] (x: H): H *: this.type =
3838
if (stageIt) stagedCons[H](x)
@@ -60,7 +60,7 @@ sealed trait Tuple extends Any {
6060
}
6161

6262
inline def stagedCons[H] (x: H): H *: this.type =
63-
${ StagedTuple.stagedCons('{this}, '{x}, constValueOpt[BoundedSize[this.type]]) }
63+
${ StagedTuple.stagedCons('this, 'x, constValueOpt[BoundedSize[this.type]]) }
6464

6565
inline def ++(that: Tuple): Concat[this.type, that.type] =
6666
if (stageIt) stagedConcat(that)
@@ -104,8 +104,8 @@ sealed trait Tuple extends Any {
104104
}
105105

106106
inline def stagedConcat(that: Tuple): Concat[this.type, that.type] =
107-
${ StagedTuple.stagedConcat('{this}, constValueOpt[BoundedSize[this.type]],
108-
'{that}, constValueOpt[BoundedSize[that.type]]) }
107+
${ StagedTuple.stagedConcat('this, constValueOpt[BoundedSize[this.type]],
108+
'that, constValueOpt[BoundedSize[that.type]]) }
109109

110110
inline def genericConcat[T <: Tuple](xs: Tuple, ys: Tuple): Tuple =
111111
fromArray[T](xs.toArray ++ ys.toArray)
@@ -121,7 +121,7 @@ sealed trait Tuple extends Any {
121121
}
122122

123123
inline def stagedSize: Size[this.type] =
124-
${ StagedTuple.sizeStaged[Size[this.type]]('{this}, constValueOpt[BoundedSize[this.type]]) }
124+
${ StagedTuple.sizeStaged[Size[this.type]]('this, constValueOpt[BoundedSize[this.type]]) }
125125
}
126126

127127
object Tuple {
@@ -217,7 +217,7 @@ object Tuple {
217217
}
218218

219219
inline def stagedFromArray[T <: Tuple](xs: Array[Object]): T =
220-
${ StagedTuple.fromArrayStaged[T]('{xs}, constValueOpt[BoundedSize[this.type]]) }
220+
${ StagedTuple.fromArrayStaged[T]('xs, constValueOpt[BoundedSize[this.type]]) }
221221

222222
def dynamicFromArray[T <: Tuple](xs: Array[Object]): T = xs.length match {
223223
case 0 => ().asInstanceOf[T]
@@ -340,7 +340,7 @@ sealed trait NonEmptyTuple extends Tuple {
340340
}
341341

342342
inline def stagedHead: Head[this.type] =
343-
${ StagedTuple.headStaged[this.type]('{this}, constValueOpt[BoundedSize[this.type]]) }
343+
${ StagedTuple.headStaged[this.type]('this, constValueOpt[BoundedSize[this.type]]) }
344344

345345
inline def tail: Tail[this.type] =
346346
if (stageIt) stagedTail
@@ -369,7 +369,7 @@ sealed trait NonEmptyTuple extends Tuple {
369369
}
370370

371371
inline def stagedTail: Tail[this.type] =
372-
${ StagedTuple.tailStaged[this.type]('{this}, constValueOpt[BoundedSize[this.type]]) }
372+
${ StagedTuple.tailStaged[this.type]('this, constValueOpt[BoundedSize[this.type]]) }
373373

374374
inline def fallbackApply(n: Int) =
375375
inline constValueOpt[n.type] match {
@@ -430,8 +430,8 @@ sealed trait NonEmptyTuple extends Tuple {
430430

431431
inline def stagedApply(n: Int): Elem[this.type, n.type] =
432432
${ StagedTuple.applyStaged[this.type, n.type](
433-
'{this}, constValueOpt[Size[this.type]],
434-
'{n}, constValueOpt[n.type]) }
433+
'this, constValueOpt[Size[this.type]],
434+
'n, constValueOpt[n.type]) }
435435
}
436436

437437
object NonEmptyTuple {

library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ trait TreeUtils {
2222
/** */
2323
private def let[T: quoted.Type, U: quoted.Type](rhs: Expr[T])(in: Expr[T] => Expr[U]): Expr[U] = '{
2424
val x = $rhs
25-
${in('{x})}
25+
${in('x)}
2626
}
2727

2828
}

tests/disabled/run/i4803d/App_2.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object Test {
1111
}
1212

1313
inline def power2(x: Double) = {
14-
inline def power(x: Double, inline n: Long) = ${ PowerMacro.powerCode('{x}, n) }
14+
inline def power(x: Double, inline n: Long) = ${ PowerMacro.powerCode('x, n) }
1515
power(x, 2)
1616
}
1717
}

tests/disabled/run/i4803d/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import scala.quoted._
33
object PowerMacro {
44
def powerCode(x: Expr[Double], n: Long): Expr[Double] =
55
if (n == 0) '{1.0}
6-
else if (n % 2 == 0) '{ val y = $x * $x; ${powerCode('{y}, n / 2)} }
6+
else if (n % 2 == 0) '{ val y = $x * $x; ${powerCode('y, n / 2)} }
77
else '{ $x * ${powerCode(x, n - 1)} }
88
}

tests/disabled/run/xml-interpolation-3/XmlQuote_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object XmlQuote {
99

1010
implicit object SCOps {
1111
inline def xml(this inline ctx: StringContext)(args: => Any*): Xml =
12-
${XmlQuote.impl(ctx, '{args})}
12+
${XmlQuote.impl(ctx, 'args)}
1313
}
1414

1515
def impl(receiver: StringContext, args: Expr[Seq[Any]]): Expr[Xml] = {

tests/neg-with-compiler/quote-run-in-macro-1/quoted_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.quoted._
33
import scala.quoted.Toolbox.Default._
44

55
object Macros {
6-
inline def foo(i: => Int): Int = ${ fooImpl('{i}) }
6+
inline def foo(i: => Int): Int = ${ fooImpl('i) }
77
def fooImpl(i: Expr[Int]): Expr[Int] = {
88
val y: Int = i.run
99
y.toExpr

tests/neg-with-compiler/quote-run-in-macro-2/quoted_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.quoted._
33
import scala.quoted.Toolbox.Default._
44

55
object Macros {
6-
inline def foo(i: => Int): Int = ${ fooImpl('{i}) }
6+
inline def foo(i: => Int): Int = ${ fooImpl('i) }
77
def fooImpl(i: Expr[Int]): Expr[Int] = {
88
val y: Int = i.run
99
y.toExpr

tests/neg/i4433.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
object Foo {
33
inline def g(inline p: Int => Boolean): Boolean = ${ // error
4-
if(p(5)) '{true}
5-
else '{false}
4+
if(p(5)) 'true
5+
else 'false
66
}
77
}

tests/neg/i4774b.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Test {
55
def loop[T](x: Expr[T])(implicit t: Type[T]): Expr[T] = '{
66
val y: $t = $x;
77
${loop[$t]( // error
8-
'{y}
8+
'y
99
)}
1010
}
1111
}

tests/neg/i4890.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import scala.quoted._
33
object Test {
44
def toExpr(x: Option[String]): Expr[String] = x match {
55
case Some(s) =>
6-
'{s} // error
6+
's // error
77
}
88
}

tests/neg/quote-interpolator-core-old.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import scala.quoted._
55
object FInterpolation {
66

77
implicit class FInterpolatorHelper(val sc: StringContext) extends AnyVal {
8-
inline def ff(arg1: Any): String = ${fInterpolation(sc, Seq('{arg1}))} // error: Inline macro method must be a static method
9-
inline def ff(arg1: Any, arg2: Any): String = ${fInterpolation(sc, Seq('{arg1}, '{arg2}))} // error: Inline macro method must be a static method
10-
inline def ff(arg1: Any, arg2: Any, arg3: Any): String = ${fInterpolation(sc, Seq('{arg1}, '{arg2}, '{arg3}))} // error: Inline macro method must be a static method
8+
inline def ff(arg1: Any): String = ${fInterpolation(sc, Seq('arg1))} // error: Inline macro method must be a static method
9+
inline def ff(arg1: Any, arg2: Any): String = ${fInterpolation(sc, Seq('arg1, 'arg2))} // error: Inline macro method must be a static method
10+
inline def ff(arg1: Any, arg2: Any, arg3: Any): String = ${fInterpolation(sc, Seq('arg1, 'arg2, 'arg3))} // error: Inline macro method must be a static method
1111
// ...
1212
}
1313

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import scala.quoted._
22

33
object Macros {
4-
inline def foo(inline i: Int, dummy: Int, j: Int): Int = ${ bar(i + 1, '{j}) } // error: i + 1 is not a parameter or field reference
4+
inline def foo(inline i: Int, dummy: Int, j: Int): Int = ${ bar(i + 1, 'j) } // error: i + 1 is not a parameter or field reference
55
def bar(x: Int, y: Expr[Int]): Expr[Int] = '{ ${x.toExpr} + $y }
66
}

tests/neg/quote-macro-splice.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object Test {
1414

1515
inline def foo3: Int = { // error
1616
val a = 1
17-
${ impl('{a}) }
17+
${ impl('a) }
1818
}
1919

2020
inline def foo4: Int = { // error

tests/neg/quote-pcp-in-arg.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import scala.quoted._
22

33
object Foo {
4-
inline def foo(x: Int): Int = ${ bar('{ '{x}; x }) } // error
4+
inline def foo(x: Int): Int = ${ bar('{ 'x; x }) } // error
55
def bar(i: Expr[Int]): Expr[Int] = i
66
}

tests/neg/quote-splice-interpret-1.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.quoted._
33

44
object Macros {
55
inline def isZero(inline n: Int): Boolean = ${ // error
6-
if (n == 0) '{true}
7-
else '{false}
6+
if (n == 0) 'true
7+
else 'false
88
}
99
}

tests/neg/quote-this.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ class Foo {
1212
}
1313

1414
inline def i(): Unit = ${ Foo.impl[Any]('{
15-
'{this} // error
15+
'this // error
1616
}) }
1717

1818
inline def j(that: Foo): Unit = ${ Foo.impl[Any]('{
19-
'{that} // error
19+
'that // error
2020
}) }
2121

2222
inline def k(): Unit = ${ Foo.impl[Any](this) } // error

tests/neg/tasty-macro-assert/quoted_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object Asserts {
1212
object Ops
1313

1414
inline def macroAssert(cond: => Boolean): Unit =
15-
${impl('{cond})}
15+
${impl('cond)}
1616

1717
def impl(cond: Expr[Boolean])(implicit reflect: Reflection): Expr[Unit] = {
1818
import reflect._

0 commit comments

Comments
 (0)