Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ast.Trees.*
import typer.Implicits.*
import typer.ImportInfo
import Variances.varianceSign
import util.SourcePosition
import util.{Chars, SourcePosition}
import scala.util.control.NonFatal
import scala.annotation.switch
import config.{Config, Feature}
Expand Down Expand Up @@ -704,22 +704,10 @@ class PlainPrinter(_ctx: Context) extends Printer {

def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D"

private def escapedChar(ch: Char): String = (ch: @switch) match {
case '\b' => "\\b"
case '\t' => "\\t"
case '\n' => "\\n"
case '\f' => "\\f"
case '\r' => "\\r"
case '"' => "\\\""
case '\'' => "\\\'"
case '\\' => "\\\\"
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch)
}

def toText(const: Constant): Text = const.tag match {
case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"")
case StringTag => stringText(Chars.escapedString(const.value.toString, quoted = true))
case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]"
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
case CharTag => literalText(Chars.escapedChar(const.charValue))
case LongTag => literalText(const.longValue.toString + "L")
case DoubleTag => literalText(const.doubleValue.toString + "d")
case FloatTag => literalText(const.floatValue.toString + "f")
Expand All @@ -741,7 +729,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
~ (if param.isTypeParam then "" else ": ")
~ toText(param.paramInfo)

protected def escapedString(str: String): String = str flatMap escapedChar
protected final def escapedString(str: String): String = Chars.escapedString(str, quoted = false)

def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep)

Expand Down
59 changes: 58 additions & 1 deletion compiler/src/dotty/tools/dotc/util/Chars.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package dotty.tools.dotc.util
import scala.annotation.switch
import Character.{LETTER_NUMBER, LOWERCASE_LETTER, OTHER_LETTER, TITLECASE_LETTER, UPPERCASE_LETTER}
import Character.{MATH_SYMBOL, OTHER_SYMBOL}
import Character.{isJavaIdentifierPart, isUnicodeIdentifierStart, isUnicodeIdentifierPart}
import Character.{isISOControl as isControl, isJavaIdentifierPart, isUnicodeIdentifierStart, isUnicodeIdentifierPart}
import java.lang.StringBuilder

/** Contains constants and classifier methods for characters */
object Chars:
Expand Down Expand Up @@ -110,3 +111,59 @@ object Chars:

/** Would the character be encoded by `NameTransformer.encode`? */
def willBeEncoded(c: Char): Boolean = !isJavaIdentifierPart(c)

private inline def requiresFormat(c: Char): Boolean = (c: @switch) match
case '\b' | '\t' | '\n' | '\f' | '\r' | '"' | '\'' | '\\' => true
case c => isControl(c)

def escapedString(text: String, quoted: Boolean): String =
inline def doBuild: String =
val b = StringBuilder(text.length + 16)
if quoted then
b.append('"')
var i = 0
while i < text.length do
escapedChar(b, text.charAt(i))
i += 1
if quoted then
b.append('"')
b.toString
var i = 0
while i < text.length do
if requiresFormat(text.charAt(i)) then return doBuild
i += 1
if quoted then "\"" + text + "\""
else text

def escapedChar(ch: Char): String =
if requiresFormat(ch) then
val b = StringBuilder().append('\'')
escapedChar(b, ch)
b.append('\'').toString
else
"'" + ch + "'"

private def escapedChar(b: StringBuilder, c: Char): Unit =
inline def quadNibble(x: Int, i: Int): Unit =
if i < 4 then
quadNibble(x >> 4, i + 1)
val n = x & 0xF
val c = if (n < 10) '0' + n else 'a' + (n - 10)
b.append(c.toChar)
val replace = (c: @switch) match
case '\b' => "\\b"
case '\t' => "\\t"
case '\n' => "\\n"
case '\f' => "\\f"
case '\r' => "\\r"
case '"' => "\\\""
case '\'' => "\\\'"
case '\\' => "\\\\"
case c =>
if isControl(c) then
b.append("\\u")
quadNibble(c.toInt, 0)
else
b.append(c)
return
b.append(replace)
34 changes: 11 additions & 23 deletions compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package scala.quoted
package runtime.impl.printers

import dotty.tools.dotc.util.Chars

import scala.annotation.switch
import scala.collection.mutable

import java.lang.StringBuilder

/** Printer for fully elaborated representation of the source code */
object SourceCode {
Expand Down Expand Up @@ -97,7 +102,7 @@ object SourceCode {
this += lineBreak() += "}"
}

def result(): String = sb.result()
def result(): String = sb.toString

private def lineBreak(): String = "\n" + (" " * indent)
private def doubleLineBreak(): String = "\n\n" + (" " * indent)
Expand Down Expand Up @@ -438,7 +443,7 @@ object SourceCode {
case _ =>
inParens {
printTree(term)
this += (if (dotty.tools.dotc.util.Chars.isOperatorPart(sb.last)) " : " else ": ")
this += (if Chars.isOperatorPart(sb.charAt(sb.length - 1)) then " : " else ": ")
def printTypeOrAnnots(tpe: TypeRepr): Unit = tpe match {
case AnnotatedType(tp, annot) if tp == term.tpe =>
printAnnotation(annot)
Expand Down Expand Up @@ -957,9 +962,6 @@ object SourceCode {

}

inline private val qc = '\''
inline private val qSc = '"'

def printConstant(const: Constant): this.type = const match {
case UnitConstant() => this += highlightLiteral("()")
case NullConstant() => this += highlightLiteral("null")
Expand All @@ -970,8 +972,8 @@ object SourceCode {
case LongConstant(v) => this += highlightLiteral(v.toString + "L")
case FloatConstant(v) => this += highlightLiteral(v.toString + "f")
case DoubleConstant(v) => this += highlightLiteral(v.toString)
case CharConstant(v) => this += highlightString(s"${qc}${escapedChar(v)}${qc}")
case StringConstant(v) => this += highlightString(s"${qSc}${escapedString(v)}${qSc}")
case CharConstant(v) => this += highlightString(Chars.escapedChar(v))
case StringConstant(v) => this += highlightString(Chars.escapedString(v, quoted = true))
case ClassOfConstant(v) =>
this += "classOf"
inSquare(printType(v))
Expand Down Expand Up @@ -1445,22 +1447,8 @@ object SourceCode {
private def +=(x: Char): this.type = { sb.append(x); this }
private def +=(x: String): this.type = { sb.append(x); this }

private def escapedChar(ch: Char): String = (ch: @switch) match {
case '\b' => "\\b"
case '\t' => "\\t"
case '\n' => "\\n"
case '\f' => "\\f"
case '\r' => "\\r"
case '"' => "\\\""
case '\'' => "\\\'"
case '\\' => "\\\\"
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch)
}

private def escapedString(str: String): String = str flatMap escapedChar

private val names = collection.mutable.Map.empty[Symbol, String]
private val namesIndex = collection.mutable.Map.empty[String, Int]
private val names = mutable.Map.empty[Symbol, String]
private val namesIndex = mutable.Map.empty[String, Int]

private def splicedName(sym: Symbol): Option[String] = {
if sym.owner.isClassDef then None
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/dotc/printing/PrintingTest.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dotty
package tools
package dotc
package printing

import scala.language.unsafeNulls

Expand Down
8 changes: 8 additions & 0 deletions tests/printing/untyped/strings.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[syntax trees at end of parser]] // tests/printing/untyped/strings.scala
package <empty> {
class C {
def chars = "\b\t\n\f\r\"\'\\a\u0003"
def greeting = "hello, world"
}
}

4 changes: 4 additions & 0 deletions tests/printing/untyped/strings.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

class C:
def chars = "\b\t\n\f\r\"\'\\\u0061\u0003"
def greeting = "hello, world"
Loading