@@ -930,23 +930,78 @@ object RefChecks {
930
930
val NoLevelInfo : RefChecks .OptLevelInfo = new OptLevelInfo ()
931
931
932
932
/**
933
- * Verify that references in the implicitNotFound annotation user-defined message are valid.
933
+ * Verify that references in the user-defined @implicitNotFound message are valid.
934
934
* (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
935
935
*/
936
- private def checkImplicitNotFoundAnnotation (tree : Template , sd : SymDenotation )(using Context ): Unit =
937
- for
938
- annotation <- sd.annotations if annotation.symbol == ctx.definitions.ImplicitNotFoundAnnot
939
- (arg, l) <- annotation.arguments.collect { case l@ Literal (c : Constant ) => (c.stringValue, l) }
940
- do
936
+ private object checkImplicitNotFoundAnnotation :
937
+
938
+ /**
939
+ * Warns if the class or trait has an @implicitNotFound annotation
940
+ * with invalid type variable references.
941
+ */
942
+ def template (sd : SymDenotation )(using Context ): Unit =
943
+ for
944
+ annotation <- sd.annotations
945
+ if annotation.symbol == ctx.definitions.ImplicitNotFoundAnnot
946
+ (arg, l) <- annotation.arguments.collect {
947
+ case l@ Literal (c : Constant ) => (c.stringValue, l)
948
+ }
949
+ do forEachTypeVariableReferenceIn(arg) { case (ref, start) =>
950
+ if ! sd.typeParams.exists(_.denot.name.show == ref) then
951
+ reportInvalidReferences(l, ref, start, sd)
952
+ }
953
+
954
+ /**
955
+ * Warns if the def has parameters with an @implicitNotFound annotation
956
+ * with invalid type variable references.
957
+ */
958
+ def defDef (sd : SymDenotation )(using Context ): Unit =
959
+ for
960
+ param <- sd.rawParamss.flatten
961
+ annotation <- param.annotations
962
+ if annotation.symbol == ctx.definitions.ImplicitNotFoundAnnot
963
+ (arg, l) <- annotation.arguments.collect {
964
+ case l@ Literal (c : Constant ) => (c.stringValue, l)
965
+ }
966
+ do forEachTypeVariableReferenceIn(arg) { case (ref, start) =>
967
+ if ! sd.paramSymss.flatten.exists(_.name.show == ref) then
968
+ reportInvalidReferences(l, ref, start, sd)
969
+ }
970
+
971
+ /** Reports an invalid reference to a type variable <pre>typeRef</pre> that was found in <pre>f</pre> */
972
+ private def reportInvalidReferences (
973
+ l : Literal ,
974
+ typeRef : String ,
975
+ offsetInLiteral : Int ,
976
+ sd : SymDenotation
977
+ )(using Context ) =
978
+ val msg = InvalidReferenceInImplicitNotFoundAnnotation (
979
+ typeRef, if (sd.isConstructor) " the constructor" else sd.name.show)
980
+ val span = l.span.shift(offsetInLiteral + 1 ) // +1 because of 0-based index
981
+ val pos = ctx.source.atSpan(span.withEnd(span.start))
982
+ report.warning(msg, pos)
983
+
984
+ /**
985
+ * Calls the supplied function for each quoted reference to a type variable in <pre>s</pre>.
986
+ * The input
987
+ *
988
+ * {{{
989
+ * "This is a ${T}ype re${F}erence"
990
+ * // ^0 ^12 ^22
991
+ * }}}
992
+ *
993
+ * will lead to two invocations of <pre>f</pre>, once with (T, 12) and once with (F, 22) as argument.
994
+ *
995
+ * @param s The string to query for type variable references.
996
+ * @param f A function to apply to every pair of (\<type variable>, \<position in string>).
997
+ */
998
+ private def forEachTypeVariableReferenceIn (s : String )(f : (String , Int ) => Unit ) =
941
999
// matches quoted references such as "${(A)}", "${(Abc)}", etc.
942
1000
val reference = """ (?<=\$\{)[a-zA-Z]+(?=\})""" .r
943
- val matches = reference.findAllIn(arg)
944
- for m <- matches if ! sd.typeParams.exists(_.denot.name.show == m) do
945
- val msg = InvalidReferenceInImplicitNotFoundAnnotation (m, sd.name.show)
946
- val span = l.span.shift(matches.start + 1 ) // +1 because of 0-based index
947
- val pos = ctx.source.atSpan(span.withEnd(span.start))
948
- report.warning(msg, pos)
1001
+ val matches = reference.findAllIn(s)
1002
+ for m <- matches do f(m, matches.start)
949
1003
1004
+ end checkImplicitNotFoundAnnotation
950
1005
951
1006
}
952
1007
import RefChecks ._
@@ -1024,6 +1079,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1024
1079
override def transformDefDef (tree : DefDef )(using Context ): DefDef = {
1025
1080
checkNoPrivateOverrides(tree)
1026
1081
checkDeprecatedOvers(tree)
1082
+ checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
1027
1083
tree
1028
1084
}
1029
1085
@@ -1034,7 +1090,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1034
1090
if (cls.is(Trait )) tree.parents.foreach(checkParentPrefix(cls, _))
1035
1091
checkCompanionNameClashes(cls)
1036
1092
checkAllOverrides(cls)
1037
- checkImplicitNotFoundAnnotation(tree, cls.classDenot)
1093
+ checkImplicitNotFoundAnnotation.template( cls.classDenot)
1038
1094
tree
1039
1095
}
1040
1096
catch {
0 commit comments