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