@@ -23,7 +23,7 @@ import scala.util.matching.Regex._
2323import Constants .Constant
2424
2525object RefChecks {
26- import tpd .{Tree , MemberDef , Literal , Template , DefDef }
26+ import tpd .{Tree , MemberDef , NamedArg , Literal , Template , DefDef }
2727
2828 val name : String = " refchecks"
2929
@@ -943,46 +943,58 @@ object RefChecks {
943943 * (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
944944 */
945945 private object checkImplicitNotFoundAnnotation :
946-
947946 /** Warns if the class or trait has an @implicitNotFound annotation
948947 * with invalid type variable references.
949948 */
950949 def template (sd : SymDenotation )(using Context ): Unit =
951- for
952- annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot )
953- l@ Literal (c : Constant ) <- annotation.argument(0 )
954- do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) =>
955- if ! sd.typeParams.exists(_.denot.name.show == ref) then
956- reportInvalidReferences(l, ref, start, sd)
957- }
950+ val typeParamsNames = sd.ownersIterator.toSeq.flatMap(_.typeParams).map(_.denot.name.show)
951+ checkReferences(sd, typeParamsNames)
958952
959953 /** Warns if the def has parameters with an `@implicitNotFound` annotation
960954 * with invalid type variable references.
961955 */
962956 def defDef (sd : SymDenotation )(using Context ): Unit =
957+ val typeParamsNames = sd.ownersIterator.toSeq.flatMap{ ownerSym =>
958+ ownerSym.typeParams ++ ownerSym.paramSymss.flatten.filter(_.isType)
959+ }.map(_.name.show)
960+
963961 for
964962 paramSymss <- sd.paramSymss
965963 param <- paramSymss
966- do
967- for
968- annotation <- param.getAnnotation(defn.ImplicitNotFoundAnnot )
969- l@ Literal (c : Constant ) <- annotation.argument(0 )
970- do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) =>
971- if ! sd.paramSymss.flatten.exists(_.name.show == ref) then
972- reportInvalidReferences(l, ref, start, sd)
973- }
964+ if param.isTerm
965+ do checkReferences(param.denot, typeParamsNames)
966+
967+ private object PositionedStringLiteralArgument :
968+ def unapply (tree : Tree ): Option [(String , Span )] = tree match {
969+ case l@ Literal (Constant (s : String )) => Some ((s, l.span))
970+ case NamedArg (_, l@ Literal (Constant (s : String ))) => Some ((s, l.span))
971+ case _ => None
972+ }
973+
974+ private def checkReferences (sd : SymDenotation , typeParamsNames : Seq [String ])(using Context ): Unit =
975+ for
976+ annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot )
977+ PositionedStringLiteralArgument (msg, span) <- annotation.argument(0 )
978+ do forEachTypeVariableReferenceIn(msg) { case (ref, start) =>
979+ if ! typeParamsNames.contains(ref) then
980+ reportInvalidReference(span, ref, start, sd)
981+ }
974982
975- /** Reports an invalid reference to a type variable `typeRef` that was found in `l ` */
976- private def reportInvalidReferences (
977- l : Literal ,
983+ /** Reports an invalid reference to a type variable `typeRef` that was found in `span ` */
984+ private def reportInvalidReference (
985+ span : Span ,
978986 typeRef : String ,
979- offsetInLiteral : Int ,
987+ variableOffsetinArgumentLiteral : Int ,
980988 sd : SymDenotation
981989 )(using Context ) =
982- val msg = InvalidReferenceInImplicitNotFoundAnnotation (
983- typeRef, if (sd.isConstructor) " the constructor" else sd.name.show)
984- val span = l.span.shift(offsetInLiteral + 1 ) // +1 because of 0-based index
985- val pos = ctx.source.atSpan(span.startPos)
990+ val typeRefName = s " ` $typeRef` "
991+ val ownerName =
992+ if sd.isType then s " type ` ${sd.name.show}` "
993+ else if sd.owner.isConstructor then s " the constructor of ` ${sd.owner.owner.name.show}` "
994+ else s " method ` ${sd.owner.name.show}` "
995+ val msg = InvalidReferenceInImplicitNotFoundAnnotation (typeRefName, ownerName)
996+ val startPos = span.shift(variableOffsetinArgumentLiteral + 1 ).startPos // +1 because of 0-based index
997+ val pos = ctx.source.atSpan(startPos)
986998 report.warning(msg, pos)
987999
9881000 /** Calls the supplied function for each quoted reference to a type variable in <pre>s</pre>.
@@ -999,10 +1011,15 @@ object RefChecks {
9991011 * @param f A function to apply to every pair of (\<type variable>, \<position in string>).
10001012 */
10011013 private def forEachTypeVariableReferenceIn (s : String )(f : (String , Int ) => Unit ) =
1002- // matches quoted references such as "${(A)}", "${(Abc)}", etc.
1003- val reference = """ (?<=\$\{)[a-zA-Z]+(?=\})""" .r
1004- val matches = reference.findAllIn(s)
1005- for m <- matches do f(m, matches.start)
1014+ // matches quoted references such as "${A}", "${ Abc }", etc.
1015+ val referencePattern = """ \$\{\s*([^}\s]+)\s*\}""" .r
1016+ val matches = referencePattern.findAllIn(s)
1017+ for reference <- matches do
1018+ val referenceOffset = matches.start
1019+ val prefixlessReference = reference.replaceFirst(""" \$\{\s*""" , " " )
1020+ val variableOffset = referenceOffset + reference.length - prefixlessReference.length
1021+ val variableName = prefixlessReference.replaceFirst(""" \s*\}""" , " " )
1022+ f(variableName, variableOffset)
10061023
10071024 end checkImplicitNotFoundAnnotation
10081025
0 commit comments