Skip to content

Commit 2492513

Browse files
Backport "Add note about type mismatch in automatically inserted apply argument" to LTS (#21090)
Backports #20023 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 5f7c92c + 49a97d7 commit 2492513

File tree

9 files changed

+102
-4
lines changed

9 files changed

+102
-4
lines changed

compiler/src/dotty/tools/dotc/reporting/ExploringReporter.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class ExploringReporter extends StoreReporter(null, fromTyperState = false):
1818
override def removeBufferedMessages(using Context): List[Diagnostic] =
1919
try infos.toList finally reset()
2020

21+
override def mapBufferedMessages(f: Diagnostic => Diagnostic)(using Context): Unit =
22+
infos.mapInPlace(f)
23+
2124
def reset(): Unit = infos.clear()
2225

23-
end ExploringReporter
26+
end ExploringReporter

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ abstract class Reporter extends interfaces.ReporterResult {
270270
/** If this reporter buffers messages, remove and return all buffered messages. */
271271
def removeBufferedMessages(using Context): List[Diagnostic] = Nil
272272

273+
/** If this reporter buffers messages, apply `f` to all buffered messages. */
274+
def mapBufferedMessages(f: Diagnostic => Diagnostic)(using Context): Unit = ()
275+
273276
/** Issue all messages in this reporter to next outer one, or make sure they are written. */
274277
def flush()(using Context): Unit =
275278
val msgs = removeBufferedMessages

compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class StoreReporter(outer: Reporter | Null = Reporter.NoReporter, fromTyperState
2121

2222
protected var infos: mutable.ListBuffer[Diagnostic] | Null = null
2323

24-
def doReport(dia: Diagnostic)(using Context): Unit = {
24+
override def doReport(dia: Diagnostic)(using Context): Unit = {
2525
typr.println(s">>>> StoredError: ${dia.message}") // !!! DEBUG
2626
if (infos == null) infos = new mutable.ListBuffer
2727
infos.uncheckedNN += dia
@@ -37,6 +37,9 @@ class StoreReporter(outer: Reporter | Null = Reporter.NoReporter, fromTyperState
3737
if (infos != null) try infos.uncheckedNN.toList finally infos = null
3838
else Nil
3939

40+
override def mapBufferedMessages(f: Diagnostic => Diagnostic)(using Context): Unit =
41+
if infos != null then infos.uncheckedNN.mapInPlace(f)
42+
4043
override def pendingMessages(using Context): List[Diagnostic] =
4144
if (infos != null) infos.uncheckedNN.toList else Nil
4245

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ extends NotFoundMsg(MissingIdentID) {
287287
}
288288
}
289289

290-
class TypeMismatch(val found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context)
290+
class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tree], addenda: => String*)(using Context)
291291
extends TypeMismatchMsg(found, expected)(TypeMismatchID):
292292

293293
def msg(using Context) =

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,33 @@ trait Applications extends Compatibility {
10201020
}
10211021
}
10221022

1023+
/** If the applied function is an automatically inserted `apply`
1024+
* method and one of its arguments has a type mismatch , append
1025+
* a note to the error message that explains where the required
1026+
* type comes from. See #19680 and associated test case.
1027+
*/
1028+
def maybeAddInsertedApplyNote(failedState: TyperState, fun1: Tree)(using Context): Unit =
1029+
if fun1.symbol.name == nme.apply && fun1.span.isSynthetic then
1030+
fun1 match
1031+
case Select(qualifier, _) =>
1032+
def mapMessage(dia: Diagnostic): Diagnostic =
1033+
dia match
1034+
case dia: Diagnostic.Error =>
1035+
dia.msg match
1036+
case msg: TypeMismatch =>
1037+
msg.inTree match
1038+
case Some(arg) if tree.args.exists(_.span == arg.span) =>
1039+
val noteText =
1040+
i"""The required type comes from a parameter of the automatically
1041+
|inserted `apply` method of `${qualifier.tpe}`.""".stripMargin
1042+
Diagnostic.Error(msg.appendExplanation("\n\n" + noteText), dia.pos)
1043+
case _ => dia
1044+
case msg => dia
1045+
case dia => dia
1046+
failedState.reporter.mapBufferedMessages(mapMessage)
1047+
case _ => ()
1048+
else ()
1049+
10231050
fun1.tpe match {
10241051
case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs()).withType(err)
10251052
case TryDynamicCallType =>
@@ -1080,7 +1107,11 @@ trait Applications extends Compatibility {
10801107
simpleApply(fun1, proto)
10811108
} {
10821109
(failedVal, failedState) =>
1083-
def fail = { failedState.commit(); failedVal }
1110+
def fail =
1111+
maybeAddInsertedApplyNote(failedState, fun1)
1112+
failedState.commit()
1113+
failedVal
1114+
10841115
// Try once with original prototype and once (if different) with tupled one.
10851116
// The reason we need to try both is that the decision whether to use tupled
10861117
// or not was already taken but might have to be revised when an implicit

tests/neg/19680.check

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- [E007] Type Mismatch Error: tests/neg/19680.scala:9:67 --------------------------------------------------------------
2+
9 |def renderWidget(using Config): Unit = renderWebsite("/tmp")(Config()) // error: found Config, required Int
3+
| ^^^^^^^^
4+
| Found: Config
5+
| Required: Int
6+
|---------------------------------------------------------------------------------------------------------------------
7+
| Explanation (enabled by `-explain`)
8+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9+
|
10+
| Tree: new Config()
11+
| I tried to show that
12+
| Config
13+
| conforms to
14+
| Int
15+
| but none of the attempts shown below succeeded:
16+
|
17+
| ==> Config <: Int = false
18+
|
19+
| The tests were made under the empty constraint
20+
|
21+
| The required type comes from a parameter of the automatically
22+
| inserted `apply` method of `scala.collection.StringOps`.
23+
---------------------------------------------------------------------------------------------------------------------

tests/neg/19680.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options -explain
2+
3+
// Tests that the error message indicates that the required type `Int` comes
4+
// from the automatically inserted `apply` method of `String`. This note is
5+
// inserted by `insertedApplyNote` in `Applications`.
6+
7+
class Config()
8+
def renderWebsite(path: String)(using config: Config): String = ???
9+
def renderWidget(using Config): Unit = renderWebsite("/tmp")(Config()) // error: found Config, required Int

tests/neg/19680b.check

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- [E007] Type Mismatch Error: tests/neg/19680b.scala:2:21 -------------------------------------------------------------
2+
2 |def Test = List(1,2)("hello") // error: found String, required Int
3+
| ^^^^^^^
4+
| Found: ("hello" : String)
5+
| Required: Int
6+
|---------------------------------------------------------------------------------------------------------------------
7+
| Explanation (enabled by `-explain`)
8+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9+
|
10+
| Tree: "hello"
11+
| I tried to show that
12+
| ("hello" : String)
13+
| conforms to
14+
| Int
15+
| but none of the attempts shown below succeeded:
16+
|
17+
| ==> ("hello" : String) <: Int
18+
| ==> String <: Int = false
19+
|
20+
| The tests were made under the empty constraint
21+
|
22+
| The required type comes from a parameter of the automatically
23+
| inserted `apply` method of `List[Int]`.
24+
---------------------------------------------------------------------------------------------------------------------

tests/neg/19680b.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
//> using options -explain
2+
def Test = List(1,2)("hello") // error: found String, required Int

0 commit comments

Comments
 (0)