@@ -23,7 +23,7 @@ import scala.util.matching.Regex._
23
23
import Constants .Constant
24
24
25
25
object RefChecks {
26
- import tpd .{Tree , MemberDef , Literal , Template , DefDef }
26
+ import tpd .{Tree , MemberDef , NamedArg , Literal , Template , DefDef }
27
27
28
28
val name : String = " refchecks"
29
29
@@ -943,46 +943,58 @@ object RefChecks {
943
943
* (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
944
944
*/
945
945
private object checkImplicitNotFoundAnnotation :
946
-
947
946
/** Warns if the class or trait has an @implicitNotFound annotation
948
947
* with invalid type variable references.
949
948
*/
950
949
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)
958
952
959
953
/** Warns if the def has parameters with an `@implicitNotFound` annotation
960
954
* with invalid type variable references.
961
955
*/
962
956
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
+
963
961
for
964
962
paramSymss <- sd.paramSymss
965
963
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
+ }
974
982
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 ,
978
986
typeRef : String ,
979
- offsetInLiteral : Int ,
987
+ variableOffsetinArgumentLiteral : Int ,
980
988
sd : SymDenotation
981
989
)(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)
986
998
report.warning(msg, pos)
987
999
988
1000
/** Calls the supplied function for each quoted reference to a type variable in <pre>s</pre>.
@@ -999,10 +1011,15 @@ object RefChecks {
999
1011
* @param f A function to apply to every pair of (\<type variable>, \<position in string>).
1000
1012
*/
1001
1013
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)
1006
1023
1007
1024
end checkImplicitNotFoundAnnotation
1008
1025
0 commit comments