Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
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
17 changes: 14 additions & 3 deletions src/compiler/scala/tools/reflect/FastStringInterpolator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,22 @@ trait FastStringInterpolator extends FormatInterpolator {
val emptyLit = treatedContents.isEmpty
if (i < numLits - 1) {
val arg = argsIndexed(i)
if (linting && !(arg.tpe =:= definitions.StringTpe))
def warn(msg: String) = runReporting.warning(arg.pos, msg, WFlagTostringInterpolated, c.internal.enclosingOwner)
def stringlyBranches = arg match {
case If(_, thenp, elsep) => thenp.tpe <:< definitions.StringTpe && elsep.tpe <:< definitions.StringTpe
Copy link
Member

@lrytz lrytz Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-unit primitives should be allowed

scala> def f(b: Boolean) = s"b: ${if (b) 1 else 0}"
                                  ^
       warning: interpolation uses toString
def f(b: Boolean): String

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also nesting..

scala> def f(x: Any) = s"r: ${ x match { case s: String => s; case _ => if (x == null) "n" else "s" } }"
                                 ^
       warning: interpolation uses toString

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I guess!

case Match(_, cases) =>
cases.forall {
case CaseDef(_, _, body) => body.tpe <:< definitions.StringTpe
}
case _ => false
}
if (linting && !(arg.tpe =:= definitions.StringTpe)) // literals are widened, so include Null, Nothing
if (arg.tpe.typeSymbol eq definitions.UnitClass)
runReporting.warning(arg.pos, "interpolated Unit value", WFlagTostringInterpolated, c.internal.enclosingOwner)
warn("interpolated Unit value")
else if ((arg.tpe.typeSymbol eq definitions.AnyClass) && stringlyBranches)
() // if or match assumes expected type Any of interpolated expression, check branches are strings
else if (!definitions.isPrimitiveValueType(arg.tpe))
runReporting.warning(arg.pos, "interpolation uses toString", WFlagTostringInterpolated, c.internal.enclosingOwner)
warn("interpolation uses toString")
concatArgs += arg
}
if (!emptyLit) concatArgs += lit
Expand Down
51 changes: 26 additions & 25 deletions test/files/neg/tostring-interpolated.check
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
tostring-interpolated.scala:7: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.f
tostring-interpolated.scala:8: warning: interpolation uses toString
def f = f"$c" // warn
^
tostring-interpolated.scala:8: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.s
tostring-interpolated.scala:9: warning: interpolation uses toString
def s = s"$c" // warn
^
tostring-interpolated.scala:9: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.r
tostring-interpolated.scala:10: warning: interpolation uses toString
def r = raw"$c" // warn
^
tostring-interpolated.scala:11: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.format
tostring-interpolated.scala:12: warning: interpolation uses toString
def format = f"${c.x}%d in $c or $c%s" // warn using c.toString // warn
^
tostring-interpolated.scala:11: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.format
tostring-interpolated.scala:12: warning: interpolation uses toString
def format = f"${c.x}%d in $c or $c%s" // warn using c.toString // warn
^
tostring-interpolated.scala:13: warning: Boolean format is null test for non-Boolean
def bool = f"$c%b" // warn just a null check
^
tostring-interpolated.scala:15: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.oops
def oops = s"${null} slipped thru my fingers" // warn
^
tostring-interpolated.scala:20: error: interpolation uses toString
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=T.greeting
tostring-interpolated.scala:17: warning: interpolation uses toString
def greeting = s"$sb, world" // warn
^
tostring-interpolated.scala:31: error: interpolated Unit value
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=Mitigations.unitized
tostring-interpolated.scala:21: warning: Boolean format is null test for non-Boolean
def bool = f"$c%b" // warn just a null check (quirk of Java format)
^
tostring-interpolated.scala:23: warning: interpolation uses toString
def oops = s"${null} slipped thru my fingers" // warn although conforms to String
^
tostring-interpolated.scala:25: warning: interpolation uses toString
def exceptionally = s"Hello, ${???}" // warn although conforms to String
^
tostring-interpolated.scala:36: warning: interpolated Unit value
def unitized = s"unfortunately $shown" // warn accidental unit value
^
tostring-interpolated.scala:32: error: interpolated Unit value
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=w-flag-tostring-interpolated, site=Mitigations.funitized
tostring-interpolated.scala:37: warning: interpolated Unit value
def funitized = f"unfortunately $shown" // warn accidental unit value
^
1 warning
9 errors
tostring-interpolated.scala:53: warning: interpolation uses toString
val greeting = s"Hello ${if (shouldCaps) "WORLD" else world}" // warn
^
tostring-interpolated.scala:64: warning: interpolation uses toString
val greeting = s"Hello ${x match { case 42 => "WORLD" case 27 => world case _ => ??? }}" // warn
^
error: No warnings can be incurred under -Werror.
13 warnings
1 error
48 changes: 42 additions & 6 deletions test/files/neg/tostring-interpolated.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//> using options -Wconf:cat=w-flag-tostring-interpolated:e -Wtostring-interpolated
//> using options -Werror -Wtostring-interpolated
///> using options -Wconf:cat=w-flag-tostring-interpolated:e -Wtostring-interpolated

case class C(x: Int)

Expand All @@ -10,21 +11,25 @@ trait T {

def format = f"${c.x}%d in $c or $c%s" // warn using c.toString // warn

def bool = f"$c%b" // warn just a null check

def oops = s"${null} slipped thru my fingers" // warn

def ok = s"${c.toString}"

def sb = new StringBuilder().append("hello")
def greeting = s"$sb, world" // warn

def literally = s"Hello, ${"world"}" // nowarn literal, widened to String

def bool = f"$c%b" // warn just a null check (quirk of Java format)

def oops = s"${null} slipped thru my fingers" // warn although conforms to String

def exceptionally = s"Hello, ${???}" // warn although conforms to String
}

class Mitigations {

val s = "hello, world"
val i = 42
def shown() = println("shown")
def shown = println("shown") // nowarn parenless Unit def because that is at refchecks

def ok = s"$s is ok"
def jersey = s"number $i"
Expand All @@ -34,3 +39,34 @@ class Mitigations {
def nopct = f"$s is ok"
def nofmt = f"number $i"
}

class Branches {

class C {
val shouldCaps = true
val greeting = s"Hello ${if (shouldCaps) "WORLD" else "world"}"
}

class D {
val shouldCaps = true
object world { override def toString = "world" }
val greeting = s"Hello ${if (shouldCaps) "WORLD" else world}" // warn
}

class E {
def x = 42
val greeting = s"Hello ${x match { case 42 => "WORLD" case 27 => "world" case _ => ??? }}"
}

class F {
def x = 42
object world { override def toString = "world" }
val greeting = s"Hello ${x match { case 42 => "WORLD" case 27 => world case _ => ??? }}" // warn
}

class Z {
val shouldCaps = true
val greeting = s"Hello ${if (shouldCaps) ??? else null}" // nowarn quirk, no string result
}

}