Skip to content

Commit 3fe124c

Browse files
author
Niklas Vest
committed
Implement def param annotation checks & cleanup
1 parent 9e20f8b commit 3fe124c

File tree

3 files changed

+82
-27
lines changed

3 files changed

+82
-27
lines changed

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -930,23 +930,78 @@ object RefChecks {
930930
val NoLevelInfo: RefChecks.OptLevelInfo = new OptLevelInfo()
931931

932932
/**
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.
934934
* (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
935935
*/
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) =
941999
// matches quoted references such as "${(A)}", "${(Abc)}", etc.
9421000
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)
9491003

1004+
end checkImplicitNotFoundAnnotation
9501005

9511006
}
9521007
import RefChecks._
@@ -1024,6 +1079,7 @@ class RefChecks extends MiniPhase { thisPhase =>
10241079
override def transformDefDef(tree: DefDef)(using Context): DefDef = {
10251080
checkNoPrivateOverrides(tree)
10261081
checkDeprecatedOvers(tree)
1082+
checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
10271083
tree
10281084
}
10291085

@@ -1034,7 +1090,7 @@ class RefChecks extends MiniPhase { thisPhase =>
10341090
if (cls.is(Trait)) tree.parents.foreach(checkParentPrefix(cls, _))
10351091
checkCompanionNameClashes(cls)
10361092
checkAllOverrides(cls)
1037-
checkImplicitNotFoundAnnotation(tree, cls.classDenot)
1093+
checkImplicitNotFoundAnnotation.template(cls.classDenot)
10381094
tree
10391095
}
10401096
catch {

tests/neg-custom-args/fatal-warnings/i4008.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@
1818
| ^
1919
| Invalid reference to a type variable "Abc" found in the annotation argument.
2020
| The variable does not occur in the signature of ShouldWarn5.
21+
-- [E157] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:44:54 --------------------------------------
22+
44 |class C[A](using @annotation.implicitNotFound("No C[${B}] found") c: Class[A]) // error
23+
| ^
24+
| Invalid reference to a type variable "B" found in the annotation argument.
25+
| The variable does not occur in the signature of the constructor.
26+
-- [E157] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:46:62 --------------------------------------
27+
46 |def someMethod1[A](using @annotation.implicitNotFound("No C[${B}] found") sc: C[A]) = 0 // error
28+
| ^
29+
| Invalid reference to a type variable "B" found in the annotation argument.
30+
| The variable does not occur in the signature of someMethod1.

tests/neg-custom-args/fatal-warnings/i4008.scala

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,12 @@ trait ShouldntWarn3[A, B]
3737
@annotation.implicitNotFound("An implicit ShouldntWarn4[${Hello},${World}] is not in scope")
3838
class ShouldntWarn4[Hello, World]
3939

40-
4140
// ===== DefDef param annotations =====
4241

4342

4443
@annotation.implicitNotFound("Hopefully you don't see this!")
45-
class C[A]
46-
47-
object x {
48-
def someMethod[A](using @annotation.implicitNotFound("No C[${B}] found") sc: C[A]) = ???
49-
}
50-
51-
52-
// ===== use site =====
44+
class C[A](using @annotation.implicitNotFound("No C[${B}] found") c: Class[A]) // error
5345

46+
def someMethod1[A](using @annotation.implicitNotFound("No C[${B}] found") sc: C[A]) = 0 // error
5447

55-
@main def run = {
56-
// println(implicitly[C[String]])
57-
println("Hai")
58-
// someMethod
59-
}
48+
def someMethod2[A](using @annotation.implicitNotFound("No C[${A}] found") sc: C[A]) = ""

0 commit comments

Comments
 (0)