From a4cb81fbc8399671d967c247bd2136c54134519a Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Thu, 13 Nov 2025 01:49:20 +0100 Subject: [PATCH] chore: `StringContext` methods are not macros Scala 2 macros anymore --- .../tools/dotc/core/SymDenotations.scala | 4 - .../tools/dotc/core/tasty/TreeUnpickler.scala | 10 +- .../tools/dotc/transform/PostTyper.scala | 5 +- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +- library/src/scala/StringContext.scala | 6 +- tests/neg/override-scala2-macro.check | 8 +- tests/neg/override-scala2-macro.scala | 3 - .../override-scala2-macro/A_1_c2.13.16.scala | 5 + tests/neg/override-scala2-macro/B_2.scala | 3 + .../lib-StringContext/StringContext.scala | 488 ------------------ tests/run-macros/lib-StringContext/Test.scala | 8 - 11 files changed, 18 insertions(+), 528 deletions(-) delete mode 100644 tests/neg/override-scala2-macro.scala create mode 100644 tests/neg/override-scala2-macro/A_1_c2.13.16.scala create mode 100644 tests/neg/override-scala2-macro/B_2.scala delete mode 100644 tests/run-macros/lib-StringContext/StringContext.scala delete mode 100644 tests/run-macros/lib-StringContext/Test.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3b198ea4dbaa..dfc36d46b637 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1059,10 +1059,6 @@ object SymDenotations { /** Is this a Scala 2 macro defined */ final def isScala2MacroInScala3(using Context): Boolean = is(Macro, butNot = Inline) && flagsUNSAFE.is(Erased) // flag is set initially for macros - we check if it's a scala 2 macro before completing the type constructor so do not force the info to check the flag - // Consider the macros of StringContext as plain Scala 2 macros when - // compiling the standard library with Dotty. - // This should be removed on Scala 3.x - && owner.ne(defn.StringContextClass) /** An erased value or an erased inline method or field */ def isErased(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index aa1fbf371fec..5ad7a78da0ac 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -672,16 +672,8 @@ class TreeUnpickler(reader: TastyReader, if sym.owner.isClass then newLocalDummy(sym.owner) else sym.owner sym.annotations = annotFns.map(_(annotOwner)) if sym.isOpaqueAlias then sym.setFlag(Deferred) - val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased) ctx.owner match { - case cls: ClassSymbol if !isScala2MacroDefinedInScala3 || cls == defn.StringContextClass => - // Enter all members of classes that are not Scala 2 macros. - // - // For `StringContext`, enter `s`, `f` and `raw` - // These definitions will be entered when defined in Scala 2. It is fine to enter them - // as they are intrinsic macros and are specially handled by the compiler. - // Dual macro definitions will not work on `StringContext` as we would enter the symbol twice. - // But dual macros will never be needed for those definitions due to their intinsic nature. + case cls: ClassSymbol if !sym.isScala2MacroInScala3 => cls.enter(sym) case _ => } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 29baf816da5e..2fdf27945385 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -263,10 +263,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => andAlso = defn.NonBeanParamAccessorAnnots) else sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) - if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value && - sym != defn.StringContext_raw && - sym != defn.StringContext_f && - sym != defn.StringContext_s then + if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature) // Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala // This should be removed on Scala 3.x diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 164ff411e73b..c964fb3858db 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4740,11 +4740,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val inlined = Inlines.inlineCall(tree) if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) else inlined - else if (tree.symbol.isScala2Macro && - // `raw`, `f` and `s` are eliminated by the StringInterpolatorOpt phase - tree.symbol != defn.StringContext_raw && - tree.symbol != defn.StringContext_f && - tree.symbol != defn.StringContext_s) + else if (tree.symbol.isScala2Macro) if (ctx.settings.XignoreScala2Macros.value) { report.warning("Scala 2 macro cannot be used in Dotty, this call will crash at runtime. See https://docs.scala-lang.org/scala3/reference/dropped-features/macros.html", tree.srcPos.startPos) Throw(New(defn.MatchErrorClass.typeRef, Literal(Constant(s"Reached unexpanded Scala 2 macro call to ${tree.symbol.showFullName} compiled with -Xignore-scala2-macros.")) :: Nil)) diff --git a/library/src/scala/StringContext.scala b/library/src/scala/StringContext.scala index 8dbcdcf03639..63c9ad565437 100644 --- a/library/src/scala/StringContext.scala +++ b/library/src/scala/StringContext.scala @@ -91,7 +91,7 @@ case class StringContext(parts: String*) { * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, * use of a StringBuilder. */ - def s(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateS + def s(args: Any*): String = ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateS object s { /** The simple string matcher. * @@ -154,7 +154,7 @@ case class StringContext(parts: String*) { * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, * use of a StringBuilder. */ - def raw(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateRaw + def raw(args: Any*): String = ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateRaw @deprecated("Use the static method StringContext.standardInterpolator instead of the instance method", "2.13.0") def standardInterpolator(process: String => String, args: Seq[Any]): String = scStandardInterpolator(process, args, parts) @@ -194,7 +194,7 @@ case class StringContext(parts: String*) { * 2. Any `%` characters not in formatting positions must begin one of the conversions * `%%` (the literal percent) or `%n` (the platform-specific line separator). */ - def f[A >: Any](args: A*): String = macro ??? // fasttracked to scala.tools.reflect.FormatInterpolator::interpolateF + def f[A >: Any](args: A*): String = ??? // fasttracked to scala.tools.reflect.FormatInterpolator::interpolateF } object StringContext { diff --git a/tests/neg/override-scala2-macro.check b/tests/neg/override-scala2-macro.check index ff5e478342dd..ca078deb2ea6 100644 --- a/tests/neg/override-scala2-macro.check +++ b/tests/neg/override-scala2-macro.check @@ -1,5 +1,5 @@ --- [E164] Declaration Error: tests/neg/override-scala2-macro.scala:2:22 ------------------------------------------------ -2 | override inline def f[A >: Any](args: A*): String = ??? // error +-- [E164] Declaration Error: tests/neg/override-scala2-macro/B_2.scala:2:22 -------------------------------------------- +2 | override inline def foo: Unit = ??? // error | ^ - |error overriding method f in class StringContext of type [A >: Any](args: Seq[A]): String; - | method f of type [A >: Any](args: Seq[A]): String cannot be used here - only Scala-2 macros can override Scala-2 macros + | error overriding method foo in class A of type => Unit; + | method foo of type => Unit cannot be used here - only Scala-2 macros can override Scala-2 macros diff --git a/tests/neg/override-scala2-macro.scala b/tests/neg/override-scala2-macro.scala deleted file mode 100644 index 0bb048fdb0c0..000000000000 --- a/tests/neg/override-scala2-macro.scala +++ /dev/null @@ -1,3 +0,0 @@ -class Foo extends StringContext { - override inline def f[A >: Any](args: A*): String = ??? // error -} diff --git a/tests/neg/override-scala2-macro/A_1_c2.13.16.scala b/tests/neg/override-scala2-macro/A_1_c2.13.16.scala new file mode 100644 index 000000000000..3f0cd43ab29e --- /dev/null +++ b/tests/neg/override-scala2-macro/A_1_c2.13.16.scala @@ -0,0 +1,5 @@ +import scala.language.experimental.macros + +class A { + def foo: Unit = macro ??? +} diff --git a/tests/neg/override-scala2-macro/B_2.scala b/tests/neg/override-scala2-macro/B_2.scala new file mode 100644 index 000000000000..a1689524eab6 --- /dev/null +++ b/tests/neg/override-scala2-macro/B_2.scala @@ -0,0 +1,3 @@ +class B extends A { + override inline def foo: Unit = ??? // error +} diff --git a/tests/run-macros/lib-StringContext/StringContext.scala b/tests/run-macros/lib-StringContext/StringContext.scala deleted file mode 100644 index f59f00c7db08..000000000000 --- a/tests/run-macros/lib-StringContext/StringContext.scala +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Scala (https://www.scala-lang.org) - * - * Copyright EPFL and Lightbend, Inc. - * - * Licensed under Apache License 2.0 - * (http://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - - package scala - - import java.lang.StringBuilder as JLSBuilder - import scala.annotation.tailrec - - /** This class provides the basic mechanism to do String Interpolation. - * String Interpolation allows users - * to embed variable references directly in *processed* string literals. - * Here's an example: - * {{{ - * val name = "James" - * println(s"Hello, \$name") // Hello, James - * }}} - * - * Any processed string literal is rewritten as an instantiation and - * method call against this class. For example: - * {{{ - * s"Hello, \$name" - * }}} - * - * is rewritten to be: - * - * {{{ - * StringContext("Hello, ", "").s(name) - * }}} - * - * By default, this class provides the `raw`, `s` and `f` methods as - * available interpolators. - * - * To provide your own string interpolator, create an implicit class - * which adds a method to `StringContext`. Here's an example: - * {{{ - * implicit class JsonHelper(private val sc: StringContext) extends AnyVal { - * def json(args: Any*): JSONObject = ... - * } - * val x: JSONObject = json"{ a: \$a }" - * }}} - * - * Here the `JsonHelper` extension class implicitly adds the `json` method to - * `StringContext` which can be used for `json` string literals. - * - * @param parts The parts that make up the interpolated string, - * without the expressions that get inserted by interpolation. - */ - case class StringContext(parts: String*) { - - import StringContext.{checkLengths as scCheckLengths, glob, standardInterpolator as scStandardInterpolator} - - @deprecated("use same-named method on StringContext companion object", "2.13.0") - def checkLengths(args: scala.collection.Seq[Any]): Unit = scCheckLengths(args, parts) - - /** The simple string interpolator. - * - * It inserts its arguments between corresponding parts of the string context. - * It also treats standard escape sequences as defined in the Scala specification. - * Here's an example of usage: - * {{{ - * val name = "James" - * println(s"Hello, \$name") // Hello, James - * }}} - * In this example, the expression \$name is replaced with the `toString` of the - * variable `name`. - * The `s` interpolator can take the `toString` of any arbitrary expression within - * a `\${}` block, for example: - * {{{ - * println(s"1 + 1 = \${1 + 1}") - * }}} - * will print the string `1 + 1 = 2`. - * - * @param `args` The arguments to be inserted into the resulting string. - * @throws IllegalArgumentException - * if the number of `parts` in the enclosing `StringContext` does not exceed - * the number of arguments `arg` by exactly 1. - * @throws StringContext.InvalidEscapeException - * if a `parts` string contains a backslash (`\`) character - * that does not start a valid escape sequence. - * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, - * use of a StringBuilder. - */ - def s(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateS - object s { - /** The simple string matcher. - * - * Attempts to match the input string to the given interpolated patterns via - * a naive globbing, that is the reverse of the simple interpolator. - * - * Here is an example usage: - * - * {{{ - * val s"Hello, \$name" = "Hello, James" - * println(name) // "James" - * }}} - * - * In this example, the string "James" ends up matching the location where the pattern - * `\$name` is positioned, and thus ends up bound to that variable. - * - * Multiple matches are supported: - * - * {{{ - * val s"\$greeting, \$name" = "Hello, James" - * println(greeting) // "Hello" - * println(name) // "James" - * }}} - * - * And the `s` matcher can match an arbitrary pattern within the `\${}` block, for example: - * - * {{{ - * val TimeSplitter = "([0-9]+)[.:]([0-9]+)".r - * val s"The time is \${TimeSplitter(hours, mins)}" = "The time is 10.50" - * println(hours) // 10 - * println(mins) // 50 - * }}} - * - * Here, we use the `TimeSplitter` regex within the `s` matcher, further splitting the - * matched string "10.50" into its constituent parts - */ - def unapplySeq(s: String): Option[Seq[String]] = glob(parts, s) - } - /** The raw string interpolator. - * - * It inserts its arguments between corresponding parts of the string context. - * As opposed to the simple string interpolator `s`, this one does not treat - * standard escape sequences as defined in the Scala specification. - * - * For example, the raw processed string `raw"a\nb"` is equal to the scala string `"a\\nb"`. - * - * ''Note:'' Even when using the raw interpolator, Scala will process Unicode escapes. - * Unicode processing in the raw interpolator is deprecated as of scala 2.13.2 and - * will be removed in the future - * For example: - * {{{ - * scala> raw"\u005cu0023" - * res0: String = # - * }}} - * - * @param `args` The arguments to be inserted into the resulting string. - * @throws IllegalArgumentException - * if the number of `parts` in the enclosing `StringContext` does not exceed - * the number of arguments `arg` by exactly 1. - * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, - * use of a StringBuilder. - */ - def raw(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateRaw - - @deprecated("Use the static method StringContext.standardInterpolator instead of the instance method", "2.13.0") - def standardInterpolator(process: String => String, args: Seq[Any]): String = scStandardInterpolator(process, args, parts) - - /** The formatted string interpolator. - * - * It inserts its arguments between corresponding parts of the string context. - * It also treats standard escape sequences as defined in the Scala specification. - * Finally, if an interpolated expression is followed by a `parts` string - * that starts with a formatting specifier, the expression is formatted according to that - * specifier. All specifiers allowed in Java format strings are handled, and in the same - * way they are treated in Java. - * - * For example: - * {{{ - * val height = 1.9d - * val name = "James" - * println(f"\$name%s is \$height%2.2f meters tall") // James is 1.90 meters tall - * }}} - * - * @param `args` The arguments to be inserted into the resulting string. - * @throws IllegalArgumentException - * if the number of `parts` in the enclosing `StringContext` does not exceed - * the number of arguments `arg` by exactly 1. - * @throws StringContext.InvalidEscapeException - * if a `parts` string contains a backslash (`\`) character - * that does not start a valid escape sequence. - * - * Note: The `f` method works by assembling a format string from all the `parts` strings and using - * `java.lang.String.format` to format all arguments with that format string. The format string is - * obtained by concatenating all `parts` strings, and performing two transformations: - * - * 1. Let a _formatting position_ be a start of any `parts` string except the first one. - * If a formatting position does not refer to a `%` character (which is assumed to - * start a format specifier), then the string format specifier `%s` is inserted. - * - * 2. Any `%` characters not in formatting positions must begin one of the conversions - * `%%` (the literal percent) or `%n` (the platform-specific line separator). - */ - def f[A >: Any](args: A*): String = macro ??? // fasttracked to scala.tools.reflect.FormatInterpolator::interpolateF - } - - object StringContext { - /** - * Linear time glob-matching implementation. - * Adapted from https://research.swtch.com/glob - * - * @param patternChunks The non-wildcard portions of the input pattern, - * separated by wildcards - * @param input The input you wish to match against - * @return None if there is no match, Some containing the sequence of matched - * wildcard strings if there is a match - */ - def glob(patternChunks: Seq[String], input: String): Option[Seq[String]] = { - var patternIndex = 0 - var inputIndex = 0 - var nextPatternIndex = 0 - var nextInputIndex = 0 - - val numWildcards = patternChunks.length - 1 - val matchStarts = Array.fill(numWildcards)(-1) - val matchEnds = Array.fill(numWildcards)(-1) - - val nameLength = input.length - // The final pattern is as long as all the chunks, separated by 1-character - // glob-wildcard placeholders - val patternLength = { - var n = numWildcards - for(chunk <- patternChunks) { - n += chunk.length - } - n - } - - // Convert the input pattern chunks into a single sequence of shorts; each - // non-negative short represents a character, while -1 represents a glob wildcard - val pattern = { - val arr = new Array[Short](patternLength) - var i = 0 - var first = true - for(chunk <- patternChunks) { - if (first) first = false - else { - arr(i) = -1 - i += 1 - } - for(c <- chunk) { - arr(i) = c.toShort - i += 1 - } - } - arr - } - - // Lookup table for each character in the pattern to check whether or not - // it refers to a glob wildcard; a non-negative integer indicates which - // glob wildcard it represents, while -1 means it doesn't represent any - val matchIndices = { - val arr = Array.fill(patternLength + 1)(-1) - var i = 0 - var j = 0 - for(chunk <- patternChunks) { - if (j < numWildcards) { - i += chunk.length - arr(i) = j - i += 1 - j += 1 - } - } - arr - } - - while(patternIndex < patternLength || inputIndex < nameLength) { - matchIndices(patternIndex) match { - case -1 => // do nothing - case n => - matchStarts(n) = matchStarts(n) match { - case -1 => inputIndex - case s => math.min(s, inputIndex) - } - matchEnds(n) = matchEnds(n) match { - case -1 => inputIndex - case s => math.max(s, inputIndex) - } - } - - val continue = if (patternIndex < patternLength) { - val c = pattern(patternIndex) - c match { - case -1 => // zero-or-more-character wildcard - // Try to match at nx. If that doesn't work out, restart at nx+1 next. - nextPatternIndex = patternIndex - nextInputIndex = inputIndex + 1 - patternIndex += 1 - true - case _ => // ordinary character - if (inputIndex < nameLength && input(inputIndex) == c) { - patternIndex += 1 - inputIndex += 1 - true - } else { - false - } - } - } else false - - // Mismatch. Maybe restart. - if (!continue) { - if (0 < nextInputIndex && nextInputIndex <= nameLength) { - patternIndex = nextPatternIndex - inputIndex = nextInputIndex - } else { - return None - } - } - } - - // Matched all of pattern to all of name. Success. - Some(collection.immutable.ArraySeq.unsafeWrapArray( - Array.tabulate(patternChunks.length - 1)(n => input.slice(matchStarts(n), matchEnds(n))) - )) - } - - /** An exception that is thrown if a string contains a backslash (`\`) character - * that does not start a valid escape sequence. - * @param str The offending string - * @param index The index of the offending backslash character in `str`. - */ - class InvalidEscapeException(str: String, val index: Int) extends IllegalArgumentException( - s"""invalid escape ${ - require(index >= 0 && index < str.length) - val ok = s"""[\\b, \\t, \\n, \\f, \\r, \\\\, \\", \\', \\uxxxx]""" - if (index == str.length - 1) "at terminal" else s"'\\${str(index + 1)}' not one of $ok at" - } index $index in "$str". Use \\\\ for literal \\.""" - ) - - protected[scala] class InvalidUnicodeEscapeException(str: String, val escapeStart: Int, val index: Int) extends IllegalArgumentException( - s"""invalid unicode escape at index $index of $str""" - ) - - private[this] def readUEscape(src: String, startindex: Int): (Char, Int) = { - val len = src.length() - def loop(uindex: Int): (Char, Int) = { - def loopCP(dindex: Int, codepoint: Int): (Char, Int) = { - //supports BMP + surrogate escapes - //but only in four hex-digit code units (uxxxx) - if(dindex >= 4) { - val usRead = uindex - startindex - val digitsRead = dindex - (codepoint.asInstanceOf[Char], usRead + digitsRead) - } - else if (dindex + uindex >= len) - throw new InvalidUnicodeEscapeException(src, startindex, uindex + dindex) - else { - val ch = src(dindex + uindex) - val e = ch.asDigit - if(e >= 0 && e <= 15) loopCP(dindex + 1, (codepoint << 4) + e) - else throw new InvalidUnicodeEscapeException(src, startindex, uindex + dindex) - } - } - if(uindex >= len) throw new InvalidUnicodeEscapeException(src, startindex, uindex - 1) - //allow one or more `u` characters between the - //backslash and the code unit - else if(src(uindex) == 'u') loop(uindex + 1) - else loopCP(0, 0) - } - loop(startindex) - } - - /** Expands standard Scala escape sequences in a string. - * Escape sequences are: - * control: `\b`, `\t`, `\n`, `\f`, `\r` - * escape: `\\`, `\"`, `\'` - * - * @param str A string that may contain escape sequences - * @return The string with all escape sequences expanded. - */ - @deprecated("use processEscapes", "2.13.0") - def treatEscapes(str: String): String = processEscapes(str) - - /** Expands standard Scala escape sequences in a string. - * Escape sequences are: - * control: `\b`, `\t`, `\n`, `\f`, `\r` - * escape: `\\`, `\"`, `\'` - * - * @param str A string that may contain escape sequences - * @return The string with all escape sequences expanded. - */ - def processEscapes(str: String): String = - str indexOf '\\' match { - case -1 => str - case i => replace(str, i) - } - - protected[scala] def processUnicode(str: String): String = - str indexOf "\\" match { - case i if i == -1 || i >= (str.length() - 5) => str - case i => replaceU(str, i) - } - - //replace escapes with given first escape - private[this] def replace(str: String, first: Int): String = { - val len = str.length() - val b = new JLSBuilder - // append replacement starting at index `i`, with `next` backslash - @tailrec def loop(i: Int, next: Int): String = { - if (next >= 0) { - //require(str(next) == '\\') - if (next > i) b.append(str, i, next) - var idx = next + 1 - if (idx >= len) throw new InvalidEscapeException(str, next) - val c = str(idx) match { - case 'u' => 'u' - case 'b' => '\b' - case 't' => '\t' - case 'n' => '\n' - case 'f' => '\f' - case 'r' => '\r' - case '"' => '"' - case '\'' => '\'' - case '\\' => '\\' - case _ => throw new InvalidEscapeException(str, next) - } - val (ch, advance) = if (c == 'u') readUEscape(str, idx) - else (c, 1) - idx += advance - b append ch - loop(idx, str.indexOf('\\', idx)) - } else { - if (i < len) b.append(str, i, len) - b.toString - } - } - loop(0, first) - } - - //replace escapes with given first escape - private[this] def replaceU(str: String, first: Int): String = { - val len = str.length() - val b = new JLSBuilder - // append replacement starting at index `i`, with `next` backslash - @tailrec def loop(i: Int, next: Int): String = { - if (next >= 0) { - //require(str(next) == '\\') - if (next > i) b.append(str, i, next) - var idx = next + 1 - if (idx >= len) { - if (idx == len) b.append('\\') - b.toString() - } - else { - val (ch, advance) = str(idx) match { - case 'u' => readUEscape(str, idx) - case chr => { - b.append('\\') - (chr, 1) - } - } - idx += advance - b.append(ch) - loop(idx, str.indexOf('\\', idx)) - } - } else { - if (i < len) b.append(str, i, len) - b.toString() - } - } - loop(0, first) - } - - def standardInterpolator(process: String => String, args: scala.collection.Seq[Any], parts: Seq[String]): String = { - StringContext.checkLengths(args, parts) - val pi = parts.iterator - val ai = args.iterator - val bldr = new JLSBuilder(process(pi.next())) - while (ai.hasNext) { - bldr append ai.next() - bldr append process(pi.next()) - } - bldr.toString - } - - /** Checks that the length of the given argument `args` is one less than the number - * of `parts` supplied to the `StringContext`. - * - * @throws IllegalArgumentException if this is not the case. - */ - def checkLengths(args: scala.collection.Seq[Any], parts: Seq[String]): Unit = - if (parts.length != args.length + 1) - throw new IllegalArgumentException("wrong number of arguments ("+ args.length - +") for interpolated string with "+ parts.length +" parts") - - } diff --git a/tests/run-macros/lib-StringContext/Test.scala b/tests/run-macros/lib-StringContext/Test.scala deleted file mode 100644 index 22ae9d057c9f..000000000000 --- a/tests/run-macros/lib-StringContext/Test.scala +++ /dev/null @@ -1,8 +0,0 @@ - -object Test { - def main(args: Array[String]): Unit = { - println(s"s ${1}") - println(raw"raw ${2}") - println(f"f ${3}") - } -}