From 69ce27434e07c8dc215490256fe51622d768dd7e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 18 Sep 2013 18:06:59 -0700 Subject: SI-7848 Xlint no warn on $sym with params This idea brought to you by retronym. Also improve implicitNotFound detection at typer; and avoid checking the standard interpolation expression for cases like s"some $$x". Some minor refactorings of implicitNotFound strings. The intersobralator allows extra spaces, i.e., trims. --- .../tools/nsc/typechecker/ContextErrors.scala | 13 +++++++----- .../scala/tools/nsc/typechecker/Implicits.scala | 23 +++++++++++---------- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 24 +++++++++++++--------- 4 files changed, 35 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 1f4d5cbac2..0f7fb98e66 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -145,12 +145,15 @@ trait ContextErrors { def errMsg = { val paramName = param.name val paramTp = param.tpe + def evOrParam = ( + if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) + "evidence parameter of type" + else + s"parameter $paramName:" + ) paramTp.typeSymbolDirect match { - case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) - case _ => - "could not find implicit value for "+ - (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type " - else "parameter "+paramName+": ")+paramTp + case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) + case _ => s"could not find implicit value for $evOrParam $paramTp" } } issueNormalTypeError(tree, errMsg) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index fbe8cd77fb..b30ae917d9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1405,13 +1405,15 @@ trait Implicits { case None => Some("Missing argument `msg` on implicitNotFound annotation.") }) + // http://dcsobral.blogspot.com/2010/01/string-interpolation-in-scala-with.html + private val Intersobralator = """\$\{\s*([^}\s]+)\s*\}""".r class Message(sym: Symbol, msg: String) { - // http://dcsobral.blogspot.com/2010/01/string-interpolation-in-scala-with.html - private def interpolate(text: String, vars: Map[String, String]) = { - """\$\{([^}]+)\}""".r.replaceAllIn(text, (_: Regex.Match) match { - case Regex.Groups(v) => java.util.regex.Matcher.quoteReplacement(vars.getOrElse(v, "")) // #3915: need to quote replacement string since it may include $'s (such as the interpreter's $iw) - })} + private def interpolate(text: String, vars: Map[String, String]) = + Intersobralator.replaceAllIn(text, (_: Regex.Match) match { + case Regex.Groups(v) => Regex quoteReplacement vars.getOrElse(v, "") + // #3915: need to quote replacement string since it may include $'s (such as the interpreter's $iw) + }) private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName) @@ -1420,17 +1422,16 @@ trait Implicits { interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc? def validate: Option[String] = { - // is there a shorter way to avoid the intermediate toList? - val refs = """\$\{([^}]+)\}""".r.findAllIn(msg).matchData.map(_ group 1).toSet + val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSet val decls = typeParamNames.toSet (refs &~ decls) match { case s if s.isEmpty => None - case unboundNames => + case unboundNames => val singular = unboundNames.size == 1 - Some("The type parameter"+( if(singular) " " else "s " )+ unboundNames.mkString(", ") + - " referenced in the message of the @implicitNotFound annotation "+( if(singular) "is" else "are" )+ - " not defined by "+ sym +".") + val ess = if (singular) "" else "s" + val bee = if (singular) "is" else "are" + Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @implicitNotFound annotation $bee not defined by $sym.") } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 32e908e03b..6286463715 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1458,7 +1458,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans applyChecks(sym.annotations) // validate implicitNotFoundMessage analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => - unit.warning(tree.pos, "Invalid implicitNotFound message for %s%s:\n%s".format(sym, sym.locationString, warn)) + unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") } case tpt@TypeTree() => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 157c6ba4de..b62ab54359 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4875,18 +4875,22 @@ 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) { - // Unfortunately implicit not found strings looks for all the world like - // missing interpolators. - def isArgToImplicitNotFound = context.enclosingApply.tree match { - case Apply(fn, _) => fn.symbol != null && fn.symbol.enclClass == ImplicitNotFoundClass - case _ => false + // 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 (_.exists)) - def suspiciousName = names find (lookUp _ andThen (_.exists)) - def lookUp(n: TermName) = context.lookupSymbol(n, !_.alternatives.exists(symRequiresArg)).symbol + 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 @@ -4901,7 +4905,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tree.value match { case Constant(s: String) => val noWarn = ( - isArgToImplicitNotFound + isArgToCertainApply || !(s contains ' ') // another heuristic - e.g. a string with only "$asInstanceOf" ) if (!noWarn) warnAbout(s) -- cgit v1.2.3