Skip to content

Commit bd05d37

Browse files
authored
Merge pull request #9731 from som-snytt/issue/9719
Multivalued setting can be set multiply
2 parents 9f7e416 + 0772585 commit bd05d37

File tree

4 files changed

+100
-42
lines changed

4 files changed

+100
-42
lines changed

compiler/src/dotty/tools/dotc/config/Settings.scala

+33-28
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package dotty.tools.dotc
22
package config
33

4-
import collection.mutable.ArrayBuffer
5-
import scala.util.{ Success, Failure }
6-
import reflect.ClassTag
74
import core.Contexts._
8-
import scala.annotation.tailrec
9-
import dotty.tools.io.{ AbstractFile, Directory, JarArchive, PlainDirectory }
105

11-
// import annotation.unchecked
12-
// Dotty deviation: Imports take precedence over definitions in enclosing package
13-
// (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives
14-
// us a package, which is not what was intended anyway).
6+
import dotty.tools.io.{AbstractFile, Directory, JarArchive, PlainDirectory}
7+
8+
import annotation.tailrec
9+
import collection.mutable.ArrayBuffer
1510
import language.existentials
11+
import reflect.ClassTag
12+
import scala.util.{Success, Failure}
1613

1714
object Settings {
1815

@@ -25,7 +22,7 @@ object Settings {
2522
val OutputTag: ClassTag[AbstractFile] = ClassTag(classOf[AbstractFile])
2623

2724
class SettingsState(initialValues: Seq[Any]) {
28-
private var values = ArrayBuffer(initialValues: _*)
25+
private val values = ArrayBuffer(initialValues: _*)
2926
private var _wasRead: Boolean = false
3027

3128
override def toString: String = s"SettingsState(values: ${values.toList})"
@@ -91,38 +88,46 @@ object Settings {
9188

9289
def isDefaultIn(state: SettingsState): Boolean = valueIn(state) == default
9390

91+
def isMultivalue: Boolean = implicitly[ClassTag[T]] == ListTag
92+
9493
def legalChoices: String =
95-
if (choices.isEmpty) ""
96-
else choices match {
97-
case r: Range => s"${r.head}..${r.last}"
98-
case xs: List[?] => xs.mkString(", ")
94+
choices match {
95+
case xs if xs.isEmpty => ""
96+
case r: Range => s"${r.head}..${r.last}"
97+
case xs: List[?] => xs.toString
9998
}
10099

101100
def isLegal(arg: Any): Boolean =
102-
if (choices.isEmpty)
103-
arg match {
104-
case _: T => true
105-
case _ => false
106-
}
107-
else choices match {
101+
choices match {
102+
case xs if xs.isEmpty =>
103+
arg match {
104+
case _: T => true
105+
case _ => false
106+
}
108107
case r: Range =>
109108
arg match {
110109
case x: Int => r.head <= x && x <= r.last
111110
case _ => false
112111
}
113112
case xs: List[?] =>
114-
xs contains arg
113+
xs.contains(arg)
115114
}
116115

117116
def tryToSet(state: ArgsSummary): ArgsSummary = {
118117
val ArgsSummary(sstate, arg :: args, errors, warnings) = state
119118
def update(value: Any, args: List[String]) =
120-
if (changed)
121-
ArgsSummary(updateIn(sstate, value), args, errors, warnings :+ s"Flag $name set repeatedly")
122-
else {
123-
changed = true
124-
ArgsSummary(updateIn(sstate, value), args, errors, warnings)
125-
}
119+
var dangers = warnings
120+
val value1 =
121+
if changed && isMultivalue then
122+
val value0 = value.asInstanceOf[List[String]]
123+
val current = valueIn(sstate).asInstanceOf[List[String]]
124+
value0.filter(current.contains).foreach(s => dangers :+= s"Setting $name set to $s redundantly")
125+
current ++ value0
126+
else
127+
if changed then dangers :+= s"Flag $name set repeatedly"
128+
value
129+
changed = true
130+
ArgsSummary(updateIn(sstate, value1), args, errors, dangers)
126131
def fail(msg: String, args: List[String]) =
127132
ArgsSummary(sstate, args, errors :+ msg, warnings)
128133
def missingArg =
@@ -229,7 +234,7 @@ object Settings {
229234
*
230235
* to get their arguments.
231236
*/
232-
protected def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = {
237+
protected[config] def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = {
233238
def stateWithArgs(args: List[String]) = ArgsSummary(state.sstate, args, state.errors, state.warnings)
234239
state.arguments match {
235240
case Nil =>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Diagnostic._
1717
* - The reporter is not flushed and the message containers capture a
1818
* `Context` (about 4MB)
1919
*/
20-
class StoreReporter(outer: Reporter) extends Reporter {
20+
class StoreReporter(outer: Reporter = Reporter.NoReporter) extends Reporter {
2121

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

Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package dotty.tools
22
package dotc
33

4+
import reporting.StoreReporter
45
import vulpix.TestConfiguration
56

6-
import org.junit.Test
7-
import org.junit.Assert._
7+
import dotty.tools.vulpix.TestConfiguration.mkClasspath
88

99
import java.nio.file._
1010

11-
import dotty.tools.vulpix.TestConfiguration.mkClasspath
11+
import org.junit.Test
12+
import org.junit.Assert._
1213

1314
class SettingsTests {
1415

15-
@Test def missingOutputDir: Unit = {
16+
@Test def missingOutputDir: Unit =
1617
val options = Array("-d", "not_here")
17-
val reporter = Main.process(options)
18+
val reporter = Main.process(options, reporter = StoreReporter())
1819
assertEquals(1, reporter.errorCount)
1920
assertEquals("'not_here' does not exist or is not a directory or .jar file", reporter.allErrors.head.message)
20-
}
2121

2222
@Test def jarOutput: Unit = {
2323
val source = "tests/pos/Foo.scala"
@@ -29,13 +29,12 @@ class SettingsTests {
2929
assertTrue(Files.exists(out))
3030
}
3131

32-
@Test def t8124: Unit = {
33-
val source = Paths.get("tests/pos/Foo.scala").normalize
32+
@Test def `t8124 Don't crash on missing argument`: Unit =
33+
val source = Paths.get("tests/pos/Foo.scala").normalize
3434
val outputDir = Paths.get("out/testSettings").normalize
35-
if (Files.notExists(outputDir)) Files.createDirectory(outputDir)
36-
val options = Array("-encoding", "-d", outputDir.toString, source.toString)
37-
val reporter = Main.process(options)
35+
if Files.notExists(outputDir) then Files.createDirectory(outputDir)
36+
// -encoding takes an arg!
37+
val options = Array("-encoding", "-d", outputDir.toString, source.toString)
38+
val reporter = Main.process(options, reporter = StoreReporter())
3839
assertEquals(1, reporter.errorCount)
39-
}
40-
4140
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package dotty.tools.dotc
2+
package config
3+
4+
import CommandLineParser.tokenize
5+
import Settings._
6+
7+
import org.junit.Test
8+
import org.junit.Assert._
9+
10+
class ScalaSettingsTests:
11+
12+
@Test def `A multistring setting is multivalued`: Unit =
13+
class SUT extends SettingGroup:
14+
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.")
15+
val sut = SUT()
16+
val args = tokenize("-language:implicitConversions,dynamics")
17+
val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil)
18+
val res = sut.processArguments(sumy, processAll = true, skipped = Nil)
19+
val set = sut.language.valueIn(res.sstate)
20+
assertEquals(1, args.length)
21+
assertTrue("No warnings!", res.warnings.isEmpty)
22+
assertTrue("No errors!", res.errors.isEmpty)
23+
assertTrue("Has the feature", set.contains("implicitConversions"))
24+
assertTrue("Has the feature", set.contains("dynamics"))
25+
26+
@Test def `t9719 Apply -language more than once`: Unit =
27+
class SUT extends SettingGroup:
28+
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.")
29+
val sut = SUT()
30+
val args = tokenize("-language:implicitConversions -language:dynamics")
31+
val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil)
32+
val res = sut.processArguments(sumy, processAll = true, skipped = Nil)
33+
val set = sut.language.valueIn(res.sstate)
34+
assertEquals(2, args.length)
35+
assertTrue("No warnings!", res.warnings.isEmpty)
36+
assertTrue("No errors!", res.errors.isEmpty)
37+
assertTrue("Has the feature", set.contains("implicitConversions"))
38+
assertTrue("Has the feature", set.contains("dynamics"))
39+
40+
@Test def `Warn if multistring element is supplied multiply`: Unit =
41+
class SUT extends SettingGroup:
42+
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.")
43+
val sut = SUT()
44+
val args = tokenize("-language:dynamics -language:implicitConversions -language:dynamics")
45+
val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil)
46+
val res = sut.processArguments(sumy, processAll = true, skipped = Nil)
47+
val set = sut.language.valueIn(res.sstate)
48+
assertEquals(3, args.length)
49+
assertEquals("Must warn", 1, res.warnings.length)
50+
assertTrue("No errors!", res.errors.isEmpty)
51+
assertTrue("Has the feature", set.contains("implicitConversions"))
52+
assertTrue("Has the feature", set.contains("dynamics"))
53+
54+
end ScalaSettingsTests

0 commit comments

Comments
 (0)