Skip to content

Commit 2c70f3d

Browse files
mboveljan-pieteriusildra
committed
Add note about type mismatch in automatically inserted apply argument
Co-Authored-By: Jan-Pieter van den Heuvel <[email protected]> Co-Authored-By: Lucas Nouguier <[email protected]>
1 parent 912d886 commit 2c70f3d

File tree

9 files changed

+103
-4
lines changed

9 files changed

+103
-4
lines changed

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

+4-1
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

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

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

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

+4-1
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ extends NotFoundMsg(MissingIdentID) {
289289
}
290290
}
291291

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

295295
def msg(using Context) =

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

+31-1
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,37 @@ trait Applications extends Compatibility {
10841084
simpleApply(fun1, proto)
10851085
} {
10861086
(failedVal, failedState) =>
1087-
def fail = { failedState.commit(); failedVal }
1087+
def fail =
1088+
insertedApplyNote()
1089+
failedState.commit()
1090+
failedVal
1091+
1092+
/** If the applied function is an automatically inserted `apply`
1093+
* method and one of its arguments has a type mismatch , append
1094+
* a note to the error message that explains where the required
1095+
* type comes from. See #19680 and associated test case.
1096+
*/
1097+
def insertedApplyNote() =
1098+
if fun1.symbol.name == nme.apply && fun1.span.isSynthetic then
1099+
fun1 match
1100+
case Select(qualifier, _) =>
1101+
failedState.reporter.mapBufferedMessages:
1102+
case dia: Diagnostic.Error =>
1103+
dia.msg match
1104+
case msg: TypeMismatch =>
1105+
msg.inTree match
1106+
case Some(arg) if tree.args.exists(_.span == arg.span) =>
1107+
val Select(qualifier, _) = fun1: @unchecked
1108+
val noteText =
1109+
i"""The required type comes from a parameter of the automatically
1110+
|inserted `apply` method of `${qualifier.tpe}`,
1111+
|which is the type of `${qualifier.show}`.""".stripMargin
1112+
Diagnostic.Error(msg.appendExplanation("\n\n" + noteText), dia.pos)
1113+
case _ => dia
1114+
case msg => dia
1115+
case dia => dia
1116+
case _ => ()
1117+
10881118
// Try once with original prototype and once (if different) with tupled one.
10891119
// The reason we need to try both is that the decision whether to use tupled
10901120
// or not was already taken but might have to be revised when an implicit

tests/neg/19680.check

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
| which is the type of `augmentString(renderWebsite("/tmp")(x$1))`.
24+
---------------------------------------------------------------------------------------------------------------------

tests/neg/19680.scala

+9
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

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
| which is the type of `List.apply[Int]([1,2 : Int]*)`.
25+
---------------------------------------------------------------------------------------------------------------------

tests/neg/19680b.scala

+2
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)