summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala63
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala21
-rw-r--r--test/files/neg/forgot-interpolator.check11
-rw-r--r--test/files/neg/forgot-interpolator.scala34
4 files changed, 85 insertions, 44 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index e0ea555fe8..2a2fd889ad 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -96,7 +96,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
private final val SYNTHETIC_PRIVATE = TRANS_FLAG
private final val InterpolatorCodeRegex = """\$\{.*?\}""".r
- private final val InterpolatorIdentRegex = """\$\w+""".r
+ private final val InterpolatorIdentRegex = """\$[$\w]+""".r // note that \w doesn't include $
abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors {
import context0.unit
@@ -4878,42 +4878,39 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
// Warn about likely interpolated strings which are missing their interpolators
- def warnMissingInterpolator(tree: Literal) = if (!isPastTyper) {
+ def warnMissingInterpolator(lit: Literal): Unit = if (!isPastTyper) {
// attempt to avoid warning about the special interpolated message string
// for implicitNotFound or any standard interpolation (with embedded $$).
- def isArgToCertainApply = context.enclosingApply.tree match {
- case Apply(Select(Apply(Ident(n), parts), _), args) if n.toTypeName == StringContextClass.name
- => true // parts contains tree
- case Apply(Select(New(Ident(n)), _), _) if n == ImplicitNotFoundClass.name
- => true
- case _ => false
- }
- def warnAbout(s: String) = {
- def names = InterpolatorIdentRegex findAllIn s map (n => TermName(n stripPrefix "$"))
- def isSuspiciousExpr = (InterpolatorCodeRegex findFirstIn s).nonEmpty
- //def isSuspiciousName = names exists (lookUp _ andThen isCandidate _)
- def suspiciousName = names find (n => isCandidate(lookUp(n)))
- def lookUp(n: TermName) = context.lookupSymbol(n, _ => true).symbol
- def isCandidate(s: Symbol) = s.exists && s.alternatives.exists(alt => !symRequiresArg(alt))
- def symRequiresArg(s: Symbol) = (
- s.paramss.nonEmpty
- && (s.paramss.head.headOption filterNot (_.isImplicit)).isDefined
- )
- val suggest = "Did you forget the interpolator?"
- if (isSuspiciousExpr)
- unit.warning(tree.pos, s"That looks like an interpolated expression! $suggest")
- else /* if (isSuspiciousName) */ suspiciousName foreach (n =>
- unit.warning(tree.pos, s"`$$$n` looks like an interpolated identifier! $suggest")
+ def isRecognizablyNotForInterpolation = context.enclosingApply.tree match {
+ case Apply(Select(Apply(RefTree(_, nme.StringContext), _), _), _) => true
+ case Apply(Select(New(RefTree(_, tpnme.implicitNotFound)), _), _) => true
+ case _ => false
+ }
+ def requiresNoArgs(tp: Type): Boolean = tp match {
+ case PolyType(_, restpe) => requiresNoArgs(restpe)
+ case MethodType(Nil, restpe) => requiresNoArgs(restpe) // may be a curried method - can't tell yet
+ case MethodType(p :: _, _) => p.isImplicit // implicit method requires no args
+ case _ => true // catches all others including NullaryMethodType
+ }
+ def isPlausible(m: Symbol) = m.alternatives exists (m => requiresNoArgs(m.info))
+
+ def maybeWarn(s: String): Unit = {
+ def warn(message: String) = context.unit.warning(lit.pos, s"$message Did you forget the interpolator?")
+ def suspiciousSym(name: TermName) = context.lookupSymbol(name, _ => true).symbol
+ def suspiciousExpr = InterpolatorCodeRegex findFirstIn s
+ def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(s drop 1))
+
+ // heuristics - no warning on e.g. a string with only "$asInstanceOf"
+ if (s contains ' ') (
+ if (suspiciousExpr.nonEmpty)
+ warn("That looks like an interpolated expression!") // "${...}"
+ else
+ suspiciousIdents find isPlausible foreach (sym => warn(s"`$$${sym.name}` looks like an interpolated identifier!")) // "$id"
)
}
- tree.value match {
- case Constant(s: String) =>
- val noWarn = (
- isArgToCertainApply
- || !(s contains ' ') // another heuristic - e.g. a string with only "$asInstanceOf"
- )
- if (!noWarn) warnAbout(s)
- case _ =>
+ lit match {
+ case Literal(Constant(s: String)) if !isRecognizablyNotForInterpolation => maybeWarn(s)
+ case _ =>
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index dc5c6704bc..4990596839 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -122,15 +122,16 @@ trait StdNames {
final val Unit: NameType = "Unit"
// some types whose companions we utilize
- final val AnyRef: NameType = "AnyRef"
- final val Array: NameType = "Array"
- final val List: NameType = "List"
- final val Seq: NameType = "Seq"
- final val Symbol: NameType = "Symbol"
- final val WeakTypeTag: NameType = "WeakTypeTag"
- final val TypeTag : NameType = "TypeTag"
- final val Expr: NameType = "Expr"
- final val String: NameType = "String"
+ final val AnyRef: NameType = "AnyRef"
+ final val Array: NameType = "Array"
+ final val List: NameType = "List"
+ final val Seq: NameType = "Seq"
+ final val Symbol: NameType = "Symbol"
+ final val WeakTypeTag: NameType = "WeakTypeTag"
+ final val TypeTag : NameType = "TypeTag"
+ final val Expr: NameType = "Expr"
+ final val String: NameType = "String"
+ final val StringContext: NameType = "StringContext"
// fictions we use as both types and terms
final val ERROR: NameType = "<error>"
@@ -237,6 +238,7 @@ trait StdNames {
final val ClassManifest: NameType = "ClassManifest"
final val Enum: NameType = "Enum"
final val Group: NameType = "Group"
+ final val implicitNotFound: NameType = "implicitNotFound"
final val Name: NameType = "Name"
final val Tree: NameType = "Tree"
final val TermName: NameType = "TermName"
@@ -600,7 +602,6 @@ trait StdNames {
val RootClass: NameType = "RootClass"
val Select: NameType = "Select"
val SelectFromTypeTree: NameType = "SelectFromTypeTree"
- val StringContext: NameType = "StringContext"
val SyntacticApplied: NameType = "SyntacticApplied"
val SyntacticAssign: NameType = "SyntacticAssign"
val SyntacticBlock: NameType = "SyntacticBlock"
diff --git a/test/files/neg/forgot-interpolator.check b/test/files/neg/forgot-interpolator.check
index 98440fe657..157cbb4802 100644
--- a/test/files/neg/forgot-interpolator.check
+++ b/test/files/neg/forgot-interpolator.check
@@ -16,6 +16,15 @@ forgot-interpolator.scala:42: warning: `$bar` looks like an interpolated identif
forgot-interpolator.scala:47: warning: `$hippo` looks like an interpolated identifier! Did you forget the interpolator?
def h = "$hippo takes an implicit" // warn 6
^
+forgot-interpolator.scala:88: warning: `$groucho` looks like an interpolated identifier! Did you forget the interpolator?
+ def f2 = "I salute $groucho" // warn 7
+ ^
+forgot-interpolator.scala:89: warning: `$dingo` looks like an interpolated identifier! Did you forget the interpolator?
+ def f3 = "I even salute $dingo" // warn 8
+ ^
+forgot-interpolator.scala:90: warning: `$calico` looks like an interpolated identifier! Did you forget the interpolator?
+ def f4 = "I also salute $calico" // warn 9
+ ^
error: No warnings can be incurred under -Xfatal-warnings.
-6 warnings found
+9 warnings found
one error found
diff --git a/test/files/neg/forgot-interpolator.scala b/test/files/neg/forgot-interpolator.scala
index e007f15009..34a7c7aef4 100644
--- a/test/files/neg/forgot-interpolator.scala
+++ b/test/files/neg/forgot-interpolator.scala
@@ -57,3 +57,37 @@ package test {
@implicitNotFound("No Z in ${A}") // no warn
class Z[A]
}
+
+
+package inf1 {
+ import scala.annotation.implicitNotFound
+
+ @implicitNotFound(msg = "Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${From}.") // no warn
+ trait CannotBuildFrom[-From, -Elem, +To]
+}
+
+package inf2 {
+ @scala.annotation.implicitNotFound(msg = "Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${From}.") // no warn
+ trait CannotBuildFrom[-From, -Elem, +To]
+}
+
+package inf3 {
+ @scala.annotation.implicitNotFound("Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${From}.") // no warn
+ trait CannotBuildFrom[-From, -Elem, +To]
+}
+
+package curry {
+ class A {
+ def bunko()(x: Int): Int = 5
+ def groucho(): Int = 5
+ def dingo()()()()()(): Int = 5 // kind of nuts this can be evaluated with just 'dingo', but okay
+ def calico[T1, T2]()()(): Int = 5 // even nutsier
+ def palomino[T1, T2]()(y: Int = 5)(): Int = 5 // even nutsier
+
+ def f1 = "I was picked up by the $bunko squad" // no warn
+ def f2 = "I salute $groucho" // warn 7
+ def f3 = "I even salute $dingo" // warn 8
+ def f4 = "I also salute $calico" // warn 9
+ def f5 = "I draw the line at $palomino" // no warn
+ }
+}