From e290481098050a6e35bc3cf0924d98109afcb97c Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 7 Apr 2025 10:45:12 -0700 Subject: [PATCH 1/2] Format interpolator messages get an ID --- .../tools/dotc/interactive/Completion.scala | 10 +- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../tools/dotc/reporting/MessageKind.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 5 + .../transform/localopt/FormatChecker.scala | 15 ++- tests/neg/f-interpolator-neg.check | 92 +++++++++---------- tests/neg/f-interpolator-tests.check | 4 +- tests/neg/fEscapes.check | 2 +- 8 files changed, 72 insertions(+), 58 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index b1c0119fb2df..a37e972ef715 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -172,12 +172,12 @@ object Completion: case _ => None private object StringContextApplication: - def unapply(path: List[tpd.Tree]): Option[tpd.Apply] = + def unapply(path: List[tpd.Tree]): Option[tpd.Apply] = path match - case tpd.Select(qual @ tpd.Apply(tpd.Select(tpd.Select(_, StdNames.nme.StringContext), _), _), _) :: _ => + case tpd.Select(qual @ tpd.Apply(tpd.Select(tpd.Select(_, nme.StringContext), _), _), _) :: _ => Some(qual) case _ => None - + /** Inspect `path` to determine the offset where the completion result should be inserted. */ def completionOffset(untpdPath: List[untpd.Tree]): Int = @@ -229,8 +229,8 @@ object Completion: // See example in dotty.tools.languageserver.CompletionTest.syntheticThis case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions case StringContextApplication(qual) => - completer.scopeCompletions ++ completer.selectionCompletions(qual) - case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => + completer.scopeCompletions ++ completer.selectionCompletions(qual) + case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => completer.selectionCompletions(qual) case tpd.Select(qual, _) :: _ => Map.empty case (tree: tpd.ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 93744197cadd..600370509c6f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -222,6 +222,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case EnumMayNotBeValueClassesID // errorNumber: 206 case IllegalUnrollPlacementID // errorNumber: 207 - unused in LTS case ExtensionHasDefaultID // errorNumber: 208 + case FormatInterpolationErrorID // errorNumber: 209 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala b/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala index bb02a08d2e46..e09dd1d6e69e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala @@ -23,6 +23,7 @@ enum MessageKind: case PotentialIssue case UnusedSymbol case Staging + case Interpolation /** Human readable message that will end up being shown to the user. * NOTE: This is only used in the situation where you have multiple words diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 7cafa04f8792..6c1f8ed939d6 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3208,3 +3208,8 @@ final class EnumMayNotBeValueClasses(sym: Symbol)(using Context) extends SyntaxM def explain(using Context) = "" end EnumMayNotBeValueClasses + +class BadFormatInterpolation(errorText: String)(using Context) extends Message(FormatInterpolationErrorID): + def kind = MessageKind.Interpolation + def msg(using Context) = errorText + def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala index 4922024b6c35..31b72e5055ba 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala @@ -12,6 +12,7 @@ import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.Phases.typerPhase +import dotty.tools.dotc.reporting.BadFormatInterpolation import dotty.tools.dotc.util.Spans.Span import dotty.tools.dotc.util.chaining.* @@ -276,10 +277,16 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List val pos = partsElems(index).sourcePos val bgn = pos.span.start + offset val fin = if end < 0 then pos.span.end else pos.span.start + end - pos.withSpan(Span(bgn, fin, bgn)) + pos.withSpan(Span(start = bgn, end = fin, point = bgn)) extension (r: report.type) - def argError(message: String, index: Int): Unit = r.error(message, args(index).srcPos).tap(_ => reported = true) - def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.error(message, partPosAt(index, offset, end)).tap(_ => reported = true) - def partWarning(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.warning(message, partPosAt(index, offset, end)).tap(_ => reported = true) + def argError(message: String, index: Int): Unit = + r.error(BadFormatInterpolation(message), args(index).srcPos) + .tap(_ => reported = true) + def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = + r.error(BadFormatInterpolation(message), partPosAt(index, offset, end)) + .tap(_ => reported = true) + def partWarning(message: String, index: Int, offset: Int, end: Int): Unit = + r.warning(BadFormatInterpolation(message), partPosAt(index, offset, end)) + .tap(_ => reported = true) end TypedFormatChecker diff --git a/tests/neg/f-interpolator-neg.check b/tests/neg/f-interpolator-neg.check index ea8df052589e..2d19649e142e 100644 --- a/tests/neg/f-interpolator-neg.check +++ b/tests/neg/f-interpolator-neg.check @@ -14,187 +14,187 @@ 7 | new StringContext("", "").f() // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | too few arguments for interpolated string --- Error: tests/neg/f-interpolator-neg.scala:11:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:11:7 ------------------------------------------------- 11 | f"$s%b" // error | ^ | Found: (s : String), Required: Boolean, Null --- Error: tests/neg/f-interpolator-neg.scala:12:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:12:7 ------------------------------------------------- 12 | f"$s%c" // error | ^ | Found: (s : String), Required: Char, Byte, Short, Int --- Error: tests/neg/f-interpolator-neg.scala:13:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:13:7 ------------------------------------------------- 13 | f"$f%c" // error | ^ | Found: (f : Double), Required: Char, Byte, Short, Int --- Error: tests/neg/f-interpolator-neg.scala:14:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:14:7 ------------------------------------------------- 14 | f"$s%x" // error | ^ | Found: (s : String), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:15:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:15:7 ------------------------------------------------- 15 | f"$b%d" // error | ^ | Found: (b : Boolean), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:16:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:16:7 ------------------------------------------------- 16 | f"$s%d" // error | ^ | Found: (s : String), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:17:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:17:7 ------------------------------------------------- 17 | f"$f%o" // error | ^ | Found: (f : Double), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:18:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:18:7 ------------------------------------------------- 18 | f"$s%e" // error | ^ | Found: (s : String), Required: Double, Float, BigDecimal --- Error: tests/neg/f-interpolator-neg.scala:19:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:19:7 ------------------------------------------------- 19 | f"$b%f" // error | ^ | Found: (b : Boolean), Required: Double, Float, BigDecimal --- Error: tests/neg/f-interpolator-neg.scala:20:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:20:9 ------------------------------------------------- 20 | f"$s%i" // error | ^ | illegal conversion character 'i' --- Error: tests/neg/f-interpolator-neg.scala:24:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:24:9 ------------------------------------------------- 24 | f"$s%+ 0,(s" // error | ^^^^^ | Illegal flag '+' --- Error: tests/neg/f-interpolator-neg.scala:25:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:25:9 ------------------------------------------------- 25 | f"$c%#+ 0,(c" // error | ^^^^^^ | Only '-' allowed for c conversion --- Error: tests/neg/f-interpolator-neg.scala:26:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:26:9 ------------------------------------------------- 26 | f"$d%#d" // error | ^ | # not allowed for d conversion --- Error: tests/neg/f-interpolator-neg.scala:27:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:27:9 ------------------------------------------------- 27 | f"$d%,x" // error | ^ | ',' only allowed for d conversion of integral types --- Error: tests/neg/f-interpolator-neg.scala:28:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:28:9 ------------------------------------------------- 28 | f"$d%+ (x" // error | ^^^ | only use '+' for BigInt conversions to o, x, X --- Error: tests/neg/f-interpolator-neg.scala:29:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:29:9 ------------------------------------------------- 29 | f"$f%,(a" // error | ^^ | ',' not allowed for a, A --- Error: tests/neg/f-interpolator-neg.scala:30:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:30:9 ------------------------------------------------- 30 | f"$t%#+ 0,(tT" // error | ^^^^^^ | Only '-' allowed for date/time conversions --- Error: tests/neg/f-interpolator-neg.scala:31:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:31:7 ------------------------------------------------- 31 | f"%-#+ 0,(n" // error | ^^^^^^^ | flags not allowed --- Error: tests/neg/f-interpolator-neg.scala:32:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:32:7 ------------------------------------------------- 32 | f"%#+ 0,(%" // error | ^^^^^^ | Illegal flag '#' --- Error: tests/neg/f-interpolator-neg.scala:36:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:36:9 ------------------------------------------------- 36 | f"$c%.2c" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:37:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:37:9 ------------------------------------------------- 37 | f"$d%.2d" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:38:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:38:7 ------------------------------------------------- 38 | f"%.2%" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:39:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:39:7 ------------------------------------------------- 39 | f"%.2n" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:40:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:40:9 ------------------------------------------------- 40 | f"$f%.2a" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:41:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:41:9 ------------------------------------------------- 41 | f"$t%.2tT" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:45:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:45:7 ------------------------------------------------- 45 | f"% Date: Sun, 27 Apr 2025 23:13:37 +0200 Subject: [PATCH 2/2] Format interpolator messages get an ID [Cherry-picked 797659803738410634a695db4c9ee8a369efaa95][modified]