Skip to content

tests: Bring tool args reading to REPL tests #14140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ object CompilationUnit {
NoSource
}
else if (!source.file.exists) {
report.error(s"not found: ${source.file.path}")
report.error(s"source file not found: ${source.file.path}")
NoSource
}
else source
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/repl/ParseResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ object Settings {
/** Reset the session to the initial state from when the repl program was
* started
*/
case object Reset extends Command {
case class Reset(arg: String) extends Command
object Reset {
val command: String = ":reset"
}

Expand All @@ -110,7 +111,7 @@ case object Help extends Command {
|:type <expression> evaluate the type of the given expression
|:doc <expression> print the documentation for the given expression
|:imports show import history
|:reset reset the repl to its initial state, forgetting all session entries
|:reset [options] reset the repl to its initial state, forgetting all session entries
|:settings <options> update compiler options, if possible
""".stripMargin
}
Expand All @@ -130,7 +131,7 @@ object ParseResult {
Quit.command -> (_ => Quit),
Quit.alias -> (_ => Quit),
Help.command -> (_ => Help),
Reset.command -> (_ => Reset),
Reset.command -> (arg => Reset(arg)),
Imports.command -> (_ => Imports),
Load.command -> (arg => Load(arg)),
TypeOf.command -> (arg => TypeOf(arg)),
Expand Down
29 changes: 14 additions & 15 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,21 @@ class ReplDriver(settings: Array[String],
override def sourcesRequired: Boolean = false

/** Create a fresh and initialized context with IDE mode enabled */
private def initialCtx = {
private def initialCtx(settings: List[String]) = {
val rootCtx = initCtx.fresh.addMode(Mode.ReadPositions | Mode.Interactive)
rootCtx.setSetting(rootCtx.settings.YcookComments, true)
rootCtx.setSetting(rootCtx.settings.YreadComments, true)
setupRootCtx(this.settings ++ settings, rootCtx)
}

private def setupRootCtx(settings: Array[String], rootCtx: Context) = {
setup(settings, rootCtx) match
case Some((files, ictx)) =>
case Some((files, ictx)) => inContext(ictx) {
shouldStart = true
ictx.base.initialize()(using ictx)
if files.nonEmpty then out.println(i"Ignoring spurious arguments: $files%, %")
ictx.base.initialize()
ictx
}
case None =>
shouldStart = false
rootCtx
Expand All @@ -93,8 +99,8 @@ class ReplDriver(settings: Array[String],
* such, when the user enters `:reset` this method should be called to reset
* everything properly
*/
protected def resetToInitial(): Unit = {
rootCtx = initialCtx
protected def resetToInitial(settings: List[String] = Nil): Unit = {
rootCtx = initialCtx(settings)
if (rootCtx.settings.outputDir.isDefault(using rootCtx))
rootCtx = rootCtx.fresh
.setSetting(rootCtx.settings.outputDir, new VirtualDirectory("<REPL compilation output>"))
Expand Down Expand Up @@ -372,8 +378,8 @@ class ReplDriver(settings: Array[String],
out.println(Help.text)
state

case Reset =>
resetToInitial()
case Reset(arg) =>
resetToInitial(tokenize(arg))
initialState

case Imports =>
Expand Down Expand Up @@ -423,14 +429,7 @@ class ReplDriver(settings: Array[String],
out.println(s"${s.name} = ${if s.value == "" then "\"\"" else s.value}")
state
case _ =>
setup(tokenize(arg).toArray, rootCtx) match
case Some((files, ictx)) =>
inContext(ictx) {
if files.nonEmpty then out.println(i"Ignoring spurious arguments: $files%, %")
ictx.base.initialize()(using ictx)
rootCtx = ictx
}
case _ =>
rootCtx = setupRootCtx(tokenize(arg).toArray, rootCtx)
state.copy(context = rootCtx)

case Quit =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// scalac: -source:future -deprecation
scala> type M[X] = X match { case Int => String case _ => Int }
-- Deprecation Warning:
1 warning found
-- Deprecation Warning: --------------------------------------------------------
1 | type M[X] = X match { case Int => String case _ => Int }
| ^
| `_` is deprecated for wildcard arguments of types: use `?` instead
scala> type N[X] = X match { case List[_] => Int }
-- Deprecation Warning:
1 warning found
-- Deprecation Warning: --------------------------------------------------------
1 | type N[X] = X match { case List[_] => Int }
| ^
| `_` is deprecated for wildcard arguments of types: use `?` instead
15 changes: 15 additions & 0 deletions compiler/test-resources/repl/reset-command
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
scala> def f(thread: Thread) = thread.stop()
there were 1 deprecation warning(s); re-run with -deprecation for details
def f(thread: Thread): Unit

scala>:reset -deprecation

scala> def f(thread: Thread) = thread.stop()
1 warning found
-- Deprecation Warning: --------------------------------------------------------
1 | def f(thread: Thread) = thread.stop()
| ^^^^^^^^^^^
|method stop in class Thread is deprecated since : see corresponding Javadoc for more information.
def f(thread: Thread): Unit

scala>
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import reporting.TestReporter
import dotty.tools.io.Directory

import java.io._
import java.nio.file.{Files, Path => JPath}
import java.nio.file.{Path => JPath}

import scala.io.Source._
import org.junit.Test

class PatmatExhaustivityTest {
Expand Down Expand Up @@ -79,22 +78,4 @@ class PatmatExhaustivityTest {

println(msg)
}

// inspect given files for tool args of the form `tool: args`
// if args string ends in close comment, drop the `*` `/`
// if split, parse the args string as command line.
// (from scala.tools.partest.nest.Runner#toolArgsFor)
private def toolArgsFor(files: List[JPath]): List[String] = {
import scala.jdk.OptionConverters._
import config.CommandLineParser.tokenize
files.flatMap { path =>
val tag = "scalac:"
val endc = "*" + "/" // be forgiving of /* scalac: ... */
def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc)
val args = scala.util.Using.resource(Files.lines(path, scala.io.Codec.UTF8.charSet))(
_.limit(10).filter(_.contains(tag)).map(stripped).findAny.toScala
)
args.map(tokenize).getOrElse(Nil)
}
}
}
10 changes: 6 additions & 4 deletions compiler/test/dotty/tools/repl/ReplTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,21 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na

val expectedOutput = lines.flatMap(filterEmpties)
val actualOutput = {
resetToInitial()
val opts = toolArgsParse(lines.take(1))
val (optsLine, inputLines) = if opts.isEmpty then ("", lines) else (lines.head, lines.drop(1))
resetToInitial(opts)

assert(lines.head.startsWith(prompt),
assert(inputLines.head.startsWith(prompt),
s"""Each script must start with the prompt: "$prompt"""")
val inputRes = lines.filter(_.startsWith(prompt))
val inputRes = inputLines.filter(_.startsWith(prompt))

val buf = new ArrayBuffer[String]
inputRes.foldLeft(initialState) { (state, input) =>
val (out, nstate) = evaluate(state, input)
out.linesIterator.foreach(buf.append)
nstate
}
buf.toList.flatMap(filterEmpties)
(optsLine :: buf.toList).flatMap(filterEmpties)
}

if !FileDiff.matches(actualOutput, expectedOutput) then
Expand Down
33 changes: 27 additions & 6 deletions compiler/test/dotty/tools/utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package tools

import java.io.File
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.{Files, Path => JPath}

import scala.io.Source
import scala.io.{Codec, Source}
import scala.reflect.ClassTag
import scala.util.Using.resource
import scala.util.chaining.given
Expand All @@ -25,11 +26,10 @@ extension (f: File) def absPath =
extension (str: String) def dropExtension =
str.reverse.dropWhile(_ != '.').drop(1).reverse

private def withFile[T](file: File)(action: Source => T): T =
resource(Source.fromFile(file, UTF_8.name))(action)

def readLines(f: File): List[String] = withFile(f)(_.getLines.toList)
def readFile(f: File): String = withFile(f)(_.mkString)
private
def withFile[T](file: File)(action: Source => T)(using Codec): T = resource(Source.fromFile(file))(action)
def readLines(f: File)(using codec: Codec = Codec.UTF8): List[String] = withFile(f)(_.getLines.toList)
def readFile(f: File)(using codec: Codec = Codec.UTF8): String = withFile(f)(_.mkString)

private object Unthrown extends ControlThrowable

Expand All @@ -43,3 +43,24 @@ def assertThrows[T <: Throwable: ClassTag](p: T => Boolean)(body: => Any): Unit
case failed: T => throw AssertionError(s"Exception failed check: $failed").tap(_.addSuppressed(failed))
case NonFatal(other) => throw AssertionError(s"Wrong exception: expected ${implicitly[ClassTag[T]]} but was ${other.getClass.getName}").tap(_.addSuppressed(other))
end assertThrows

def toolArgsFor(files: List[JPath])(using codec: Codec = Codec.UTF8): List[String] =
files.flatMap(path => toolArgsParse(readLines(path.toFile)))

// Inspect the first 10 of the given lines for compiler options of the form
// `// scalac: args`, `/* scalac: args`, ` * scalac: args`.
// If args string ends in close comment, drop the `*` `/`.
// If split, parse the args string as a command line.
// (from scala.tools.partest.nest.Runner#toolArgsFor)
def toolArgsParse(lines: List[String]): List[String] = {
val tag = "scalac:"
val endc = "*" + "/" // be forgiving of /* scalac: ... */
def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc)
val args = lines.to(LazyList).take(10).filter { s =>
s.contains("// " + tag)
|| s.contains("/* " + tag)
|| s.contains(" * " + tag)
// but avoid picking up comments like "% scalac ./a.scala" and "$ scalac a.scala"
}.map(stripped).headOption
args.map(dotc.config.CommandLineParser.tokenize).getOrElse(Nil)
}