@@ -4,7 +4,7 @@ package typer
4
4
import transform ._
5
5
import core ._
6
6
import Symbols ._ , Types ._ , Contexts ._ , Flags ._ , Names ._ , NameOps ._
7
- import StdNames ._ , Denotations ._ , SymUtils ._ , Phases ._
7
+ import StdNames ._ , Denotations ._ , SymUtils ._ , Phases ._ , SymDenotations . _
8
8
import NameKinds .DefaultGetterName
9
9
import Annotations ._
10
10
import util .Spans ._
@@ -19,9 +19,11 @@ import Decorators._
19
19
import typer .ErrorReporting ._
20
20
import config .Feature .{warnOnMigration , migrateTo3 }
21
21
import reporting ._
22
+ import scala .util .matching .Regex ._
23
+ import Constants .Constant
22
24
23
25
object RefChecks {
24
- import tpd .{Tree , MemberDef }
26
+ import tpd .{Tree , MemberDef , Literal , Template , DefDef }
25
27
26
28
val name : String = " refchecks"
27
29
@@ -936,6 +938,74 @@ object RefChecks {
936
938
}
937
939
938
940
val NoLevelInfo : RefChecks .OptLevelInfo = new OptLevelInfo ()
941
+
942
+ /** Verify that references in the user-defined `@implicitNotFound` message are valid.
943
+ * (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
944
+ */
945
+ private object checkImplicitNotFoundAnnotation :
946
+
947
+ /** Warns if the class or trait has an @implicitNotFound annotation
948
+ * with invalid type variable references.
949
+ */
950
+ 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
+ }
958
+
959
+ /** Warns if the def has parameters with an `@implicitNotFound` annotation
960
+ * with invalid type variable references.
961
+ */
962
+ def defDef (sd : SymDenotation )(using Context ): Unit =
963
+ for
964
+ paramSymss <- sd.paramSymss
965
+ 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
+ }
974
+
975
+ /** Reports an invalid reference to a type variable `typeRef` that was found in `l` */
976
+ private def reportInvalidReferences (
977
+ l : Literal ,
978
+ typeRef : String ,
979
+ offsetInLiteral : Int ,
980
+ sd : SymDenotation
981
+ )(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)
986
+ report.warning(msg, pos)
987
+
988
+ /** Calls the supplied function for each quoted reference to a type variable in <pre>s</pre>.
989
+ * The input
990
+ *
991
+ * ```scala
992
+ * "This is a ${T}ype re${F}erence"
993
+ * // ^0 ^12 ^22
994
+ * ```
995
+ *
996
+ * will lead to two invocations of `f`, once with `(T, 12)` and once with `(F, 22)` as argument.
997
+ *
998
+ * @param s The string to query for type variable references.
999
+ * @param f A function to apply to every pair of (\<type variable>, \<position in string>).
1000
+ */
1001
+ 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)
1006
+
1007
+ end checkImplicitNotFoundAnnotation
1008
+
939
1009
}
940
1010
import RefChecks ._
941
1011
@@ -1012,6 +1082,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1012
1082
override def transformDefDef (tree : DefDef )(using Context ): DefDef = {
1013
1083
checkNoPrivateOverrides(tree)
1014
1084
checkDeprecatedOvers(tree)
1085
+ checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
1015
1086
tree
1016
1087
}
1017
1088
@@ -1022,6 +1093,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1022
1093
if (cls.is(Trait )) tree.parents.foreach(checkParentPrefix(cls, _))
1023
1094
checkCompanionNameClashes(cls)
1024
1095
checkAllOverrides(cls)
1096
+ checkImplicitNotFoundAnnotation.template(cls.classDenot)
1025
1097
tree
1026
1098
}
1027
1099
catch {
0 commit comments