Skip to content

Commit

Permalink
Support missing syntax variants for compiler options
Browse files Browse the repository at this point in the history
  • Loading branch information
Gedochao committed Oct 31, 2024
1 parent 5bccb4f commit 6abc7cc
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,19 @@ object ScalacOptions {
private val scalacOptionsPurePrefixes =
Set("-V", "-W", "-X", "-Y")
private val scalacOptionsPrefixes =
Set("-g", "-language", "-opt", "-P", "-target", "-source") ++ scalacOptionsPurePrefixes
Set("-P") ++ scalacOptionsPurePrefixes
private val scalacAliasedOptions = // these options don't require being passed after -O and accept an arg
Set("-encoding", "-release", "-color", YScriptRunnerOption)
Set(
"-encoding",
"-release",
"-color",
"-g",
"-language",
"-opt",
"-target",
"-source",
YScriptRunnerOption
)
private val scalacNoArgAliasedOptions = // these options don't require being passed after -O and don't accept an arg
Set(
"-unchecked",
Expand Down Expand Up @@ -104,6 +114,9 @@ object ScalacOptions {
Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t if scalacNoArgAliasedOptions.contains(h) =>
Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t
if scalacAliasedOptions.exists(o => h.startsWith(o + ":")) &&
h.count(_ == ':') == 1 => Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t if scalacAliasedOptions.contains(h) =>
// check if the next scalac arg is a different option or a param to the current option
val maybeOptionArg = t.headOption.filter(!_.startsWith("-"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,78 @@ trait CompileScalacCompatTestDefinitions { _: CompileTestDefinitions =>
}
}

// Given the vast number of ways compiler options can be passed from the CLI,
// we test them all (or most, at the very least), as a (perhaps overkill) sanity check.
// Pieces of the existing `-language:*` test are reused, but kept separate for clarity.
{
val modes @ Seq(viaDirective, viaCli, viaCliWithExplicitOpt, mixed, mixedWithExplicitOpt) =
Seq("directive", "cli", "cli with -O", "mixed", "mixed with -O")
for {
mode <- modes
if actualScalaVersion == Constants.scala3Next
syntaxVariant <- Seq(
Seq(Seq("-color:never"), Seq("-language:noAutoTupling"), Seq("-language:strictEquality")),
Seq(
Seq("-color", "never"),
Seq("-language", "noAutoTupling"),
Seq("-language", "strictEquality")
),
Seq(Seq("-color:never"), Seq("\"-language:noAutoTupling,strictEquality\"")),
Seq(Seq("-color", "never"), Seq("-language", "\"noAutoTupling,strictEquality\""))
)
(cliOpts, directiveOpts) = {
val (initialCliOpts, initialDirectiveOpts) = mode match {
case m if m == mixed => syntaxVariant.splitAt(syntaxVariant.length - 1)
case m if m == mixedWithExplicitOpt =>
val (initialCliOpts, initialDirectiveOpts) =
syntaxVariant.splitAt(syntaxVariant.length - 1)
initialCliOpts.map(_.flatMap(o => Seq("-O", o))) -> initialDirectiveOpts
case c if c == viaCli => syntaxVariant -> Nil
case c if c == viaCliWithExplicitOpt =>
syntaxVariant.map(_.flatMap(o => Seq("-O", o))) -> Nil
case _ => Nil -> syntaxVariant
}
initialCliOpts.flatten.map(_.filter(_ != '"')) -> initialDirectiveOpts.flatten
}
cliOptsString = cliOpts.mkString(" ")
directiveOptsString = directiveOpts.mkString(" ")
includeDirective =
(mode == viaDirective || mode == mixed || mode == mixedWithExplicitOpt) && directiveOpts.nonEmpty
directiveString = if (includeDirective) s"//> using options $directiveOptsString" else ""
allOptsString = mode match {
case m if m.startsWith(mixed) =>
s"opts passed via command line: $cliOptsString, opts passed via directive: $directiveString"
case c if c.startsWith(viaCli) =>
s"opts passed via command line: $cliOptsString"
case _ =>
s"opts passed via directive: $directiveString"
}
} test(s"compiler options passed in $mode mode: $allOptsString") {
val sourceFileName = "example.scala"
TestInputs(os.rel / sourceFileName ->
s"""//> using scala $actualScalaVersion
|$directiveString
|case class Cat(name: String)
|case class Dog(name: String)
|def strictEquality(c: Cat, d: Dog):Boolean = c == d
|def takesTuple(tpl: Tuple) = ???
|def withTuple() = takesTuple(1, 2)
|""".stripMargin).fromRoot { root =>
val res = os.proc(TestUtil.cli, "compile", sourceFileName, cliOpts)
.call(cwd = root, check = false, stderr = os.Pipe)
println(res.err.trim())
expect(res.exitCode == 1)
val errOutput = res.err.trim()
val expectedStrictEqualityError =
"Values of types Cat and Dog cannot be compared with == or !="
expect(errOutput.contains(expectedStrictEqualityError))
val expectedNoAutoTuplingError =
"too many arguments for method takesTuple: (tpl: Tuple): Nothing"
expect(errOutput.trim().contains(expectedNoAutoTuplingError))
}
}
}

for {
useDirective <- Seq(true, false)
if !Properties.isWin
Expand Down

0 comments on commit 6abc7cc

Please sign in to comment.