diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index d16d15f42070..f5b566f3ba60 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -107,15 +107,19 @@ trait MessageRendering { .mkString(EOL) } + /** The source file path, line and column numbers from the given SourcePosition */ + def posFileStr(pos: SourcePosition): String = + val path = pos.source.file.path + if pos.exists then s"$path:${pos.line + 1}:${pos.column}" else path + /** The separator between errors containing the source file and error type * * @return separator containing error location and kind */ def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(using Context): String = if (pos.source != NoSourcePosition.source) hl(diagnosticLevel)({ - val pos1 = pos.nonInlined - val file = if !pos.exists then pos1.source.file.toString else - s"${pos1.source.file.toString}:${pos1.line + 1}:${pos1.column}" + val fileAndPos = posFileStr(pos.nonInlined) + val file = if fileAndPos.isEmpty || fileAndPos.endsWith(" ") then fileAndPos else s"$fileAndPos " val errId = if (message.errorId ne ErrorMessageID.NoExplanationID) { val errorNumber = message.errorId.errorNumber @@ -124,7 +128,7 @@ trait MessageRendering { val kind = if (message.kind == "") diagnosticLevel else s"${message.kind} $diagnosticLevel" - val prefix = s"-- ${errId}${kind}: $file " + val prefix = s"-- ${errId}${kind}: $file" prefix + ("-" * math.max(ctx.settings.pageWidth.value - stripColor(prefix).length, 0)) @@ -192,12 +196,13 @@ trait MessageRendering { def diagnosticLevel(dia: Diagnostic): String = dia match { - case dia: Error => "Error" case dia: FeatureWarning => "Feature Warning" case dia: DeprecationWarning => "Deprecation Warning" case dia: UncheckedWarning => "Unchecked Warning" case dia: MigrationWarning => "Migration Warning" - case dia: Warning => "Warning" - case dia: Info => "Info" + case _ => dia.level match // Diagnostic isn't sealed (e.g. created in the REPL) so provide a fallback + case interfaces.Diagnostic.ERROR => "Error" + case interfaces.Diagnostic.WARNING => "Warning" + case interfaces.Diagnostic.INFO => "Info" } } diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 81f4f584c3c7..085df909d71e 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -31,12 +31,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { private val MaxStringElements: Int = 1000 // no need to mkString billions of elements - /** A `MessageRenderer` for the REPL without file positions */ - private val messageRenderer = new MessageRendering { - override def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(using Context): String = - hl(diagnosticLevel)(s"-- $diagnosticLevel:") - } - private var myClassLoader: ClassLoader = _ private var myReplStringOf: Object => String = _ @@ -126,14 +120,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { } } - /** Formats errors using the `messageRenderer` */ - def formatError(dia: Diagnostic)(implicit state: State): Diagnostic = - new Diagnostic( - messageRenderer.messageAndPos(dia)(using state.context), - dia.pos, - dia.level - ) - def renderTypeDef(d: Denotation)(using Context): Diagnostic = infoDiagnostic("// defined " ++ d.symbol.showUser, d) diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 5d0d5ae99d95..1bd2323073ae 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -153,6 +153,7 @@ class ReplCompiler extends Compiler { private def runCompilationUnit(unit: CompilationUnit, state: State): Result[(CompilationUnit, State)] = { val ctx = state.context ctx.run.compileUnits(unit :: Nil) + ctx.run.printSummary() // this outputs "2 errors found" like normal - but we might decide that's needlessly noisy for the REPL if (!ctx.reporter.hasErrors) (unit, state).result else ctx.reporter.removeBufferedMessages(using ctx).errors diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 94e3b282ba7b..b2a8f9afb88e 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -1,6 +1,6 @@ package dotty.tools.repl -import java.io.{File => JFile, PrintStream} +import java.io.{File => JFile, PrintStream, PrintWriter} import java.nio.charset.StandardCharsets import dotty.tools.dotc.ast.Trees._ @@ -17,9 +17,10 @@ import dotty.tools.dotc.core.NameOps._ import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols.{Symbol, defn} +import dotty.tools.dotc.interfaces import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.printing.SyntaxHighlighting -import dotty.tools.dotc.reporting.{MessageRendering, StoreReporter} +import dotty.tools.dotc.reporting.{ConsoleReporter, MessageRendering, StoreReporter} import dotty.tools.dotc.reporting.{Message, Diagnostic} import dotty.tools.dotc.util.Spans.Span import dotty.tools.dotc.util.{SourceFile, SourcePosition} @@ -261,7 +262,6 @@ class ReplDriver(settings: Array[String], val warnings = newState.context.reporter .removeBufferedMessages(using newState.context) - .map(rendering.formatError) inContext(newState.context) { val (updatedState, definitions) = @@ -278,8 +278,7 @@ class ReplDriver(settings: Array[String], (definitions ++ warnings) .sorted - .map(_.msg) - .foreach(out.println) + .foreach(printDiagnostic) updatedState } @@ -422,7 +421,20 @@ class ReplDriver(settings: Array[String], /** shows all errors nicely formatted */ private def displayErrors(errs: Seq[Diagnostic])(implicit state: State): State = { - errs.map(rendering.formatError).map(_.msg).foreach(out.println) + errs.foreach(printDiagnostic) state } + + /** Like ConsoleReporter, but without file paths or real -Xprompt'ing */ + private object ReplConsoleReporter extends ConsoleReporter( + reader = null, // this short-circuits the -Xprompt display from waiting for an input + writer = new PrintWriter(out, /* autoFlush = */ true), // write to out, not Console.err + ) { + override def posFileStr(pos: SourcePosition) = "" // omit file paths + } + + /** Print warnings & errors using ReplConsoleReporter, and info straight to out */ + private def printDiagnostic(dia: Diagnostic)(implicit state: State) = dia.level match + case interfaces.Diagnostic.INFO => out.println(dia.msg) // print REPL's special info diagnostics directly to out + case _ => ReplConsoleReporter.doReport(dia)(using state.context) } diff --git a/compiler/test-resources/repl/1379 b/compiler/test-resources/repl/1379 index 4814370995a0..978aab344204 100644 --- a/compiler/test-resources/repl/1379 +++ b/compiler/test-resources/repl/1379 @@ -1,5 +1,6 @@ scala> object Foo { val bar = new Object { def baz = 1 }; bar.baz } --- Error: +-- [E008] Not Found Error: ----------------------------------------------------- 1 | object Foo { val bar = new Object { def baz = 1 }; bar.baz } | ^^^^^^^ - | value baz is not a member of Object \ No newline at end of file + | value baz is not a member of Object +1 error found diff --git a/compiler/test-resources/repl/errmsgs b/compiler/test-resources/repl/errmsgs index 8e415ed41a88..14e9d44fa0b8 100644 --- a/compiler/test-resources/repl/errmsgs +++ b/compiler/test-resources/repl/errmsgs @@ -1,42 +1,53 @@ scala> class Inv[T](x: T) // defined class Inv scala> val x: List[String] = List(1) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val x: List[String] = List(1) | ^ | Found: (1 : Int) | Required: String +longer explanation available when compiling with `-explain` +1 error found scala> val y: List[List[String]] = List(List(1)) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val y: List[List[String]] = List(List(1)) | ^ | Found: (1 : Int) | Required: String +longer explanation available when compiling with `-explain` +1 error found scala> val z: (List[String], List[Int]) = (List(1), List("a")) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val z: (List[String], List[Int]) = (List(1), List("a")) | ^ | Found: (1 : Int) | Required: String --- Error: +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val z: (List[String], List[Int]) = (List(1), List("a")) | ^^^ | Found: ("a" : String) | Required: Int +longer explanation available when compiling with `-explain` +2 errors found scala> val a: Inv[String] = new Inv(new Inv(1)) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val a: Inv[String] = new Inv(new Inv(1)) | ^^^^^^^^^^ | Found: Inv[Int] | Required: String +longer explanation available when compiling with `-explain` +1 error found scala> val b: Inv[String] = new Inv(1) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val b: Inv[String] = new Inv(1) | ^ | Found: (1 : Int) | Required: String +longer explanation available when compiling with `-explain` +1 error found scala> abstract class C { type T; val x: T; val s: Unit = { type T = String; var y: T = x; locally { def f() = { type T = Int; val z: T = y }; f() } }; } --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | abstract class C { type T; val x: T; val s: Unit = { type T = String; var y: T = x; locally { def f() = { type T = Int; val z: T = y }; f() } }; } | ^ |Found: (C.this.x : C.this.T) @@ -44,7 +55,8 @@ scala> abstract class C { type T; val x: T; val s: Unit = { type T = String; var | |where: T is a type in class C | T² is a type in the initializer of value s which is an alias of String --- Error: +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | abstract class C { type T; val x: T; val s: Unit = { type T = String; var y: T = x; locally { def f() = { type T = Int; val z: T = y }; f() } }; } | ^ |Found: (y : T) @@ -52,34 +64,47 @@ scala> abstract class C { type T; val x: T; val s: Unit = { type T = String; var | |where: T is a type in the initializer of value s which is an alias of String | T² is a type in method f which is an alias of Int +longer explanation available when compiling with `-explain` +2 errors found scala> class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr --- Error: +-- [E008] Not Found Error: ----------------------------------------------------- 1 | class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr | ^^^^^^^^ | value barr is not a member of Foo - did you mean foo.bar? +1 error found scala> val x: List[Int] = "foo" :: List(1) --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val x: List[Int] = "foo" :: List(1) | ^^^^^ | Found: ("foo" : String) | Required: Int +longer explanation available when compiling with `-explain` +1 error found scala> while ((( foo ))) {} --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | while ((( foo ))) {} | ^^^ | Not found: foo +longer explanation available when compiling with `-explain` +1 error found scala> val a: iDontExist = 1 --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | val a: iDontExist = 1 | ^^^^^^^^^^ | Not found: type iDontExist +longer explanation available when compiling with `-explain` +1 error found scala> def foo1(x: => Int) = x _ --- Error: +-- [E099] Syntax Error: -------------------------------------------------------- 1 | def foo1(x: => Int) = x _ | ^^^ |Only function types can be followed by _ but the current expression has type Int +longer explanation available when compiling with `-explain` +1 error found scala> def foo2(x: => Int): () => Int = x _ --- Error: +-- [E099] Syntax Error: -------------------------------------------------------- 1 | def foo2(x: => Int): () => Int = x _ | ^^^ |Only function types can be followed by _ but the current expression has type Int +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test-resources/repl/errorThenValid b/compiler/test-resources/repl/errorThenValid index 6f07caed9b0d..bfe97c5a7400 100644 --- a/compiler/test-resources/repl/errorThenValid +++ b/compiler/test-resources/repl/errorThenValid @@ -1,9 +1,10 @@ scala> val xs = scala.collection.mutable.ListBuffer[Int] --- Error: +-- [E081] Type Error: ---------------------------------------------------------- 1 | val xs = scala.collection.mutable.ListBuffer[Int] | ^ | Missing parameter type | | I could not infer the type of the parameter elems. +1 error found scala> val xs = scala.collection.mutable.ListBuffer[Int]() val xs: scala.collection.mutable.ListBuffer[Int] = ListBuffer() diff --git a/compiler/test-resources/repl/i13208.default.scala b/compiler/test-resources/repl/i13208.default.scala index afe9933cd6c1..586b5862a67f 100644 --- a/compiler/test-resources/repl/i13208.default.scala +++ b/compiler/test-resources/repl/i13208.default.scala @@ -1,7 +1,9 @@ scala> try 1 --- Warning: +1 warning found +-- [E000] Syntax Warning: ------------------------------------------------------ 1 | try 1 | ^^^^^ | A try without catch or finally is equivalent to putting | its body in a block; no exceptions are handled. +longer explanation available when compiling with `-explain` val res0: Int = 1 diff --git a/compiler/test-resources/repl/i1370 b/compiler/test-resources/repl/i1370 index 9ecc6baf05b7..6582e03b6539 100644 --- a/compiler/test-resources/repl/i1370 +++ b/compiler/test-resources/repl/i1370 @@ -1,5 +1,6 @@ scala> object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } } --- Error: +-- Error: ---------------------------------------------------------------------- 1 | object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } } | ^^^^^^^^^^ |constructor C1 cannot be accessed as a member of Lives.Private.C1 from class Private. +1 error found diff --git a/compiler/test-resources/repl/i2063 b/compiler/test-resources/repl/i2063 index e134c4eb9738..e1da25588cdf 100644 --- a/compiler/test-resources/repl/i2063 +++ b/compiler/test-resources/repl/i2063 @@ -1,15 +1,21 @@ scala> class Foo extends Bar // with one tab --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | class Foo extends Bar // with one tab | ^^^ | Not found: type Bar +longer explanation available when compiling with `-explain` +1 error found scala> class Foo extends Bar // with spaces --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | class Foo extends Bar // with spaces | ^^^ | Not found: type Bar +longer explanation available when compiling with `-explain` +1 error found scala> class Foo extends Bar // with tabs --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | class Foo extends Bar // with tabs | ^^^ | Not found: type Bar +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test-resources/repl/i2213 b/compiler/test-resources/repl/i2213 index 2ff87954dd8f..b4f703b42167 100644 --- a/compiler/test-resources/repl/i2213 +++ b/compiler/test-resources/repl/i2213 @@ -1,10 +1,12 @@ scala> def x --- Error: +-- [E019] Syntax Error: -------------------------------------------------------- 1 | def x | ^ | Missing return type +longer explanation available when compiling with `-explain` scala> def x: Int --- Error: +-- [E067] Syntax Error: -------------------------------------------------------- 1 | def x: Int | ^ |Declaration of method x not allowed here: only classes can have declared but undefined members +1 error found diff --git a/compiler/test-resources/repl/i2631 b/compiler/test-resources/repl/i2631 index a1cd56e372e0..a04e2cb15bc6 100644 --- a/compiler/test-resources/repl/i2631 +++ b/compiler/test-resources/repl/i2631 @@ -1,5 +1,6 @@ scala> class Foo(x : Any) { val foo : Integer = 0; def this() = { this(foo) } } --- Error: +-- Error: ---------------------------------------------------------------------- 1 | class Foo(x : Any) { val foo : Integer = 0; def this() = { this(foo) } } | ^^^ - | foo is not accessible from constructor arguments \ No newline at end of file + | foo is not accessible from constructor arguments +1 error found diff --git a/compiler/test-resources/repl/i4184 b/compiler/test-resources/repl/i4184 index ce407561354d..2c4eb7d12a6f 100644 --- a/compiler/test-resources/repl/i4184 +++ b/compiler/test-resources/repl/i4184 @@ -5,7 +5,8 @@ scala> object bar { class Foo } scala> implicit def eqFoo: CanEqual[foo.Foo, foo.Foo] = CanEqual.derived def eqFoo: CanEqual[foo.Foo, foo.Foo] scala> object Bar { new foo.Foo == new bar.Foo } --- Error: +-- Error: ---------------------------------------------------------------------- 1 | object Bar { new foo.Foo == new bar.Foo } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | Values of types foo.Foo and bar.Foo cannot be compared with == or != +1 error found diff --git a/compiler/test-resources/repl/i4217 b/compiler/test-resources/repl/i4217 index 2015f0041431..61dfcdc3a955 100644 --- a/compiler/test-resources/repl/i4217 +++ b/compiler/test-resources/repl/i4217 @@ -1,8 +1,10 @@ scala> def foo(x: Option[Int]) = x match { case None => } --- Warning: +1 warning found +-- [E029] Pattern Match Exhaustivity Warning: ---------------------------------- 1 | def foo(x: Option[Int]) = x match { case None => } | ^ | match may not be exhaustive. | | It would fail on pattern case: Some(_) -def foo(x: Option[Int]): Unit \ No newline at end of file +longer explanation available when compiling with `-explain` +def foo(x: Option[Int]): Unit diff --git a/compiler/test-resources/repl/i4566 b/compiler/test-resources/repl/i4566 index 40e0d0610579..27516a98b0c1 100644 --- a/compiler/test-resources/repl/i4566 +++ b/compiler/test-resources/repl/i4566 @@ -1,6 +1,8 @@ scala> object test { type ::[A, B]; def a: Int :: Int = ???; def b: Int = a } --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | object test { type ::[A, B]; def a: Int :: Int = ???; def b: Int = a } | ^ | Found: Int :: Int | Required: Int +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test-resources/repl/i5733 b/compiler/test-resources/repl/i5733 index 2a9ce5515467..ef578f24522f 100644 --- a/compiler/test-resources/repl/i5733 +++ b/compiler/test-resources/repl/i5733 @@ -1,8 +1,9 @@ scala> abstract class F { def f(arg: Any): Unit; override def toString = "F" } // defined class F scala> val f: F = println --- Warning: +1 warning found +-- Warning: -------------------------------------------------------------------- 1 | val f: F = println | ^^^^^^^ |method println is eta-expanded even though F does not have the @FunctionalInterface annotation. -val f: F = F \ No newline at end of file +val f: F = F diff --git a/compiler/test-resources/repl/i6474 b/compiler/test-resources/repl/i6474 index b39fa1b91a75..0957bbbe2761 100644 --- a/compiler/test-resources/repl/i6474 +++ b/compiler/test-resources/repl/i6474 @@ -9,10 +9,11 @@ val res0: (Any, Int) = (1,2) scala> ((1, 2): Foo2.T[Int][Int]): Foo2.T[Any][Int] val res1: (Any, Int) = (1,2) scala> (1, 2): Foo3.T[Int][Int] --- Error: +-- [E056] Syntax Error: -------------------------------------------------------- 1 | (1, 2): Foo3.T[Int][Int] | ^^^^^^^^^^^^^^^^ | Missing type parameter for Foo3.T[Int][Int] +1 error found scala> ((1, 2): Foo3.T[Int][Int][Int]): Foo3.T[Any][Int][Int] val res2: (Any, Int) = (1,2) scala> object Foo3 { type T[A] = [B] =>> [C] =>> (A, B) } diff --git a/compiler/test-resources/repl/i6676 b/compiler/test-resources/repl/i6676 index 301dd50d1202..e60023000b1d 100644 --- a/compiler/test-resources/repl/i6676 +++ b/compiler/test-resources/repl/i6676 @@ -1,24 +1,25 @@ scala> xml" --- Error: +-- Error: ---------------------------------------------------------------------- 1 | xml" | ^ | unclosed string literal scala> xml"" --- Error: +-- [E008] Not Found Error: ----------------------------------------------------- 1 | xml"" | ^^^^^ | value xml is not a member of StringContext +1 error found scala> xml""" --- Error: +-- Error: ---------------------------------------------------------------------- 1 | xml""" | ^ | unclosed multi-line string literal --- Error: +-- Error: ---------------------------------------------------------------------- 1 | xml""" | ^ | unclosed multi-line string literal scala> s" --- Error: +-- Error: ---------------------------------------------------------------------- 1 | s" | ^ | unclosed string literal diff --git a/compiler/test-resources/repl/i7644 b/compiler/test-resources/repl/i7644 index 8ecab9be11f3..4b6fd33acefa 100644 --- a/compiler/test-resources/repl/i7644 +++ b/compiler/test-resources/repl/i7644 @@ -1,18 +1,22 @@ scala> class T extends CanEqual --- Error: +-- [E112] Syntax Error: -------------------------------------------------------- 1 | class T extends CanEqual | ^ | Cannot extend sealed trait CanEqual in a different source file --- Error: +longer explanation available when compiling with `-explain` +-- [E056] Syntax Error: -------------------------------------------------------- 1 | class T extends CanEqual | ^^^^^^^^ | Missing type parameter for CanEqual +2 errors found scala> class T extends CanEqual --- Error: +-- [E112] Syntax Error: -------------------------------------------------------- 1 | class T extends CanEqual | ^ | Cannot extend sealed trait CanEqual in a different source file --- Error: +longer explanation available when compiling with `-explain` +-- [E056] Syntax Error: -------------------------------------------------------- 1 | class T extends CanEqual | ^^^^^^^^ | Missing type parameter for CanEqual +2 errors found diff --git a/compiler/test-resources/repl/i9227 b/compiler/test-resources/repl/i9227 index c591fa9f1e0c..45d1731dae39 100644 --- a/compiler/test-resources/repl/i9227 +++ b/compiler/test-resources/repl/i9227 @@ -1,6 +1,7 @@ scala> import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] }; def myMacroImpl[T](using Quotes): Expr[Unit] = '{}; println(myMacro[Int]) --- Error: +-- Error: ---------------------------------------------------------------------- 1 | import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] }; def myMacroImpl[T](using Quotes): Expr[Unit] = '{}; println(myMacro[Int]) | ^^^^^^^^^^^^ | Cannot call macro method myMacroImpl defined in the same source file | This location contains code that was inlined from rs$line$1:1 +1 error found diff --git a/compiler/test-resources/repl/importFromObj b/compiler/test-resources/repl/importFromObj index 4b26e59196bd..58e8c5370f72 100644 --- a/compiler/test-resources/repl/importFromObj +++ b/compiler/test-resources/repl/importFromObj @@ -5,20 +5,24 @@ scala> object o { val xs = List(1, 2, 3) } // defined object o scala> import o._ scala> buf += xs --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | buf += xs | ^^ | Found: (o.xs : List[Int]) | Required: Int +longer explanation available when compiling with `-explain` +1 error found scala> buf ++= xs val res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) scala> import util.foobar --- Error: +-- [E008] Not Found Error: ----------------------------------------------------- 1 | import util.foobar | ^^^^^^ | value foobar is not a member of util +1 error found scala> import util.foobar.bar --- Error: +-- [E008] Not Found Error: ----------------------------------------------------- 1 | import util.foobar.bar | ^^^^^^^^^^^ | value foobar is not a member of util +1 error found diff --git a/compiler/test-resources/repl/notFound b/compiler/test-resources/repl/notFound index 46c700f0412a..e6b251d8b723 100644 --- a/compiler/test-resources/repl/notFound +++ b/compiler/test-resources/repl/notFound @@ -1,10 +1,14 @@ scala> Foo --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | Foo | ^^^ | Not found: Foo +longer explanation available when compiling with `-explain` +1 error found scala> Bar --- Error: +-- [E006] Not Found Error: ----------------------------------------------------- 1 | Bar | ^^^ - | Not found: Bar \ No newline at end of file + | Not found: Bar +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test-resources/repl/nowarn.scala b/compiler/test-resources/repl/nowarn.scala index 63b9c4f3aa63..3236d60f9805 100644 --- a/compiler/test-resources/repl/nowarn.scala +++ b/compiler/test-resources/repl/nowarn.scala @@ -1,24 +1,30 @@ scala> @annotation.nowarn def f = try 1 // @nowarn doesn't work on first line, ctx.run is null in issueIfNotSuppressed --- Warning: +1 warning found +-- [E000] Syntax Warning: ------------------------------------------------------ 1 | @annotation.nowarn def f = try 1 // @nowarn doesn't work on first line, ctx.run is null in issueIfNotSuppressed | ^^^^^ | A try without catch or finally is equivalent to putting | its body in a block; no exceptions are handled. +longer explanation available when compiling with `-explain` def f: Int scala> @annotation.nowarn def f = try 1 def f: Int scala> def f = try 1 --- Warning: +1 warning found +-- [E000] Syntax Warning: ------------------------------------------------------ 1 | def f = try 1 | ^^^^^ | A try without catch or finally is equivalent to putting | its body in a block; no exceptions are handled. +longer explanation available when compiling with `-explain` def f: Int scala> @annotation.nowarn def f = { 1; 2 } def f: Int scala> def f = { 1; 2 } --- Warning: +1 warning found +-- [E129] Potential Issue Warning: --------------------------------------------- 1 | def f = { 1; 2 } | ^ |A pure expression does nothing in statement position; you may be omitting necessary parentheses +longer explanation available when compiling with `-explain` def f: Int diff --git a/compiler/test-resources/repl/overrides b/compiler/test-resources/repl/overrides index 35dc779bf446..a32d4b18d404 100644 --- a/compiler/test-resources/repl/overrides +++ b/compiler/test-resources/repl/overrides @@ -1,12 +1,16 @@ scala> class B { override def foo(i: Int): Unit = {}; } --- Error: +-- [E037] Declaration Error: --------------------------------------------------- 1 | class B { override def foo(i: Int): Unit = {}; } | ^ | method foo overrides nothing +longer explanation available when compiling with `-explain` +1 error found scala> class A { def foo: Unit = {}; } // defined class A scala> class B extends A { override def foo(i: Int): Unit = {}; } --- Error: +-- [E038] Declaration Error: --------------------------------------------------- 1 | class B extends A { override def foo(i: Int): Unit = {}; } | ^ | method foo has a different signature than the overridden declaration +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test-resources/repl/parsing b/compiler/test-resources/repl/parsing index 9c8955f2effc..76cdd488ca36 100644 --- a/compiler/test-resources/repl/parsing +++ b/compiler/test-resources/repl/parsing @@ -9,7 +9,7 @@ scala> 1;; 2 val res3: Int = 1 val res4: Int = 2 scala> } --- Error: +-- [E040] Syntax Error: -------------------------------------------------------- 1 | } | ^ | eof expected, but '}' found diff --git a/compiler/test-resources/type-printer/type-mismatch b/compiler/test-resources/type-printer/type-mismatch index 30bbea0c7050..363065320b9d 100644 --- a/compiler/test-resources/type-printer/type-mismatch +++ b/compiler/test-resources/type-printer/type-mismatch @@ -3,8 +3,10 @@ scala> case class Foo[A](a: A) scala> Foo(1) val res0: Foo[Int] = Foo(1) scala> val x: Foo[String] = res0 --- Error: +-- [E007] Type Mismatch Error: ------------------------------------------------- 1 | val x: Foo[String] = res0 | ^^^^ | Found: (res0 : Foo[Int]) | Required: Foo[String] +longer explanation available when compiling with `-explain` +1 error found diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index f39c1642a6b6..454fe7fb34f8 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -39,9 +39,9 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na extension [A](state: State) def andThen(op: State => A): A = op(state) - def testFile(f: JFile): Unit = testScript(f.toString, readLines(f)) + def testFile(f: JFile): Unit = testScript(f.toString, readLines(f), Some(f)) - def testScript(name: => String, lines: List[String]): Unit = { + def testScript(name: => String, lines: List[String], scriptFile: Option[JFile] = None): Unit = { val prompt = "scala>" def evaluate(state: State, input: String) = @@ -80,12 +80,19 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na } if !FileDiff.matches(actualOutput, expectedOutput) then - println("expected =========>") - println(expectedOutput.mkString(EOL)) - println("actual ===========>") - println(actualOutput.mkString(EOL)) - - fail(s"Error in script $name, expected output did not match actual") + // Some tests aren't file-based but just pass a string, so can't update anything then + // Also the files here are the copies in target/ not the original, so you need to vimdiff/mv them... + if dotty.Properties.testsUpdateCheckfile && scriptFile != None then + val checkFile = scriptFile.get + FileDiff.dump(checkFile.toPath.toString, actualOutput) + println(s"Wrote updated script file to $checkFile") + else + println("expected =========>") + println(expectedOutput.mkString(EOL)) + println("actual ===========>") + println(actualOutput.mkString(EOL)) + + fail(s"Error in script $name, expected output did not match actual") end if } } diff --git a/compiler/test/dotty/tools/repl/ShadowingTests.scala b/compiler/test/dotty/tools/repl/ShadowingTests.scala index 3af6c02cf320..4284c44c58de 100644 --- a/compiler/test/dotty/tools/repl/ShadowingTests.scala +++ b/compiler/test/dotty/tools/repl/ShadowingTests.scala @@ -78,10 +78,11 @@ class ShadowingTests extends ReplTest(options = ShadowingTests.options): shadowed = "class C(val c: Int)", script = """|scala> new C().c - |-- Error: + |-- Error: ---------------------------------------------------------------------- |1 | new C().c | | ^^^^^^^ | | missing argument for parameter c of constructor C in class C: (c: Int): C + |1 error found | |scala> new C(13).c |val res0: Int = 13 diff --git a/project/Build.scala b/project/Build.scala index 670928fc2d6d..085ee1e7f3ca 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -150,6 +150,8 @@ object Build { // Used to run binaries similar to ./bin/scala script val scala = inputKey[Unit]("run compiled binary using the correct classpath, or the user supplied classpath") + val repl = taskKey[Unit]("spawns a repl with the correct classpath") + // Compiles the documentation and static site val genDocs = inputKey[Unit]("run scaladoc to generate static documentation site") @@ -196,9 +198,7 @@ object Build { // enable verbose exception messages for JUnit (Test / testOptions) += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "-s"), - ) ++ - // Spawns a repl with the correct classpath - addCommandAlias("repl", "scala3-compiler-bootstrapped/console") + ) // Settings shared globally (scoped in Global). Used in build.sbt lazy val globalSettings = Def.settings( @@ -753,7 +753,9 @@ object Build { "scala3-tasty-inspector" -> (LocalProject("scala3-tasty-inspector") / Compile / packageBin).value.getAbsolutePath, "tasty-core" -> (LocalProject("tasty-core-bootstrapped") / Compile / packageBin).value.getAbsolutePath, ) - } + }, + repl := (Compile / console).value, + Compile / console / scalacOptions := Nil, // reset so that we get stock REPL behaviour! E.g. avoid -unchecked being enabled ) def dottyCompilerSettings(implicit mode: Mode): sbt.Def.SettingsDefinition = @@ -1725,6 +1727,7 @@ object Build { // non-bootstrapped compiler), so publish the bootstrapped one by // default. addCommandAlias("publishLocal", "scala3-bootstrapped/publishLocal"), + repl := (`scala3-compiler-bootstrapped` / repl).value, ). settings( publish / skip := true