@@ -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,81 @@ object RefChecks {
926
928
}
927
929
928
930
val NoLevelInfo : RefChecks .OptLevelInfo = new OptLevelInfo ()
931
+
932
+ /**
933
+ * Verify that references in the user-defined @implicitNotFound message are valid.
934
+ * (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
935
+ */
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.paramSymss.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>l</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 ) =
999
+ // matches quoted references such as "${(A)}", "${(Abc)}", etc.
1000
+ val reference = """ (?<=\$\{)[a-zA-Z]+(?=\})""" .r
1001
+ val matches = reference.findAllIn(s)
1002
+ for m <- matches do f(m, matches.start)
1003
+
1004
+ end checkImplicitNotFoundAnnotation
1005
+
929
1006
}
930
1007
import RefChecks ._
931
1008
@@ -1002,6 +1079,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1002
1079
override def transformDefDef (tree : DefDef )(using Context ): DefDef = {
1003
1080
checkNoPrivateOverrides(tree)
1004
1081
checkDeprecatedOvers(tree)
1082
+ checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
1005
1083
tree
1006
1084
}
1007
1085
@@ -1012,6 +1090,7 @@ class RefChecks extends MiniPhase { thisPhase =>
1012
1090
if (cls.is(Trait )) tree.parents.foreach(checkParentPrefix(cls, _))
1013
1091
checkCompanionNameClashes(cls)
1014
1092
checkAllOverrides(cls)
1093
+ checkImplicitNotFoundAnnotation.template(cls.classDenot)
1015
1094
tree
1016
1095
}
1017
1096
catch {
0 commit comments