From 0a6dd09d7585448d0c66835a2d2618eb12a47786 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Sun, 24 Aug 2014 10:22:20 +0200 Subject: Fix assertThrows, and the behaviors that it shadowed The test in StdNamesTest was introduced in cff8b569, where newTermName would throw on a negative length. In b090f97 this was changed to fix the negative length, but the test was not adapted (as it didn't fail). --- test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala | 4 ++-- test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala | 4 ++-- test/junit/scala/tools/nsc/symtab/StdNamesTest.scala | 8 ++++++-- test/junit/scala/tools/testing/AssertUtil.scala | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala index 355771bf04..d424f12710 100644 --- a/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala +++ b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala @@ -47,7 +47,7 @@ class CannotHaveAttrsTest { assertEquals(t.tpe, NoType) } - @Test + @Test @org.junit.Ignore // SI-8816 def nonDefaultPosAssignmentFails = { val pos = new OffsetPosition(null, 0) attrlessTrees.foreach { t => @@ -56,7 +56,7 @@ class CannotHaveAttrsTest { } } - @Test + @Test @org.junit.Ignore // SI-8816 def nonDefaultTpeAssignmentFails = { val tpe = typeOf[Int] attrlessTrees.foreach { t => diff --git a/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala b/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala index cf09abdfff..effbfb2f7c 100644 --- a/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala +++ b/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala @@ -36,7 +36,7 @@ class FreshNameExtractorTest { } } - @Test + @Test @org.junit.Ignore // SI-8818 def extractionsFailsIfNameDoesntEndWithNumber = { val Creator = new FreshNameCreator(prefixes.head) val Extractor = new FreshNameExtractor(prefixes.head) @@ -44,4 +44,4 @@ class FreshNameExtractorTest { val Extractor(_) = TermName(Creator.newName("foo") + "bar") } } -} \ No newline at end of file +} diff --git a/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala b/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala index 4a39cf9d48..524d2e45e0 100644 --- a/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala +++ b/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala @@ -15,11 +15,15 @@ class StdNamesTest { @Test def testNewTermNameInvalid(): Unit = { - assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, 0, -1)) - assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, 0, 0)) assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, -1, 1)) } + @Test + def testNewTermNameNegativeLenght(): Unit = { + assertEquals(nme.EMPTY, newTermName("foo".toCharArray, 0, -1)) + assertEquals(nme.EMPTY, newTermName("foo".toCharArray, 0, 0)) + } + @Test def testUnspecializedName(): Unit = { def test(expected: Name, nme: Name) { diff --git a/test/junit/scala/tools/testing/AssertUtil.scala b/test/junit/scala/tools/testing/AssertUtil.scala index 9a97c5114f..9b4833d46b 100644 --- a/test/junit/scala/tools/testing/AssertUtil.scala +++ b/test/junit/scala/tools/testing/AssertUtil.scala @@ -19,6 +19,8 @@ object AssertUtil { val clazz = manifest.runtimeClass if (!clazz.isAssignableFrom(e.getClass)) throw e + else return } + throw new AssertionError("Expression did not throw!") } } -- cgit v1.2.3 From b562d965dc30bb1fdd9433a6675bfe8e38b8c667 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 22 Aug 2014 14:55:08 +0200 Subject: -Ystatistics accepts a list of phases, cleanups in MultiChoiceSetting MultiChoiceSetting and Xlint with its deprecated aliases is now a bit simpler, but there's still room for improvement, as noted in comments. --- src/compiler/scala/tools/nsc/Global.scala | 2 +- src/compiler/scala/tools/nsc/MainBench.scala | 2 +- .../scala/tools/nsc/backend/opt/Inliners.scala | 2 +- .../tools/nsc/settings/AbsScalaSettings.scala | 2 +- .../scala/tools/nsc/settings/MutableSettings.scala | 122 ++++++++++--- .../scala/tools/nsc/settings/ScalaSettings.scala | 25 ++- .../scala/tools/nsc/settings/Warnings.scala | 194 ++++++++------------- .../scala/tools/nsc/typechecker/Infer.scala | 2 +- .../scala/tools/nsc/util/StatisticsInfo.scala | 4 +- .../scala/tools/nsc/settings/SettingsTest.scala | 63 ++++++- 10 files changed, 254 insertions(+), 164 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 82ffb35c3f..452081cff1 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1372,7 +1372,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) runCheckers() // output collected statistics - if (settings.Ystatistics) + if (settings.YstatisticsEnabled) statistics.print(phase) advancePhase() diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala index 03190a63f3..eb56c8a230 100644 --- a/src/compiler/scala/tools/nsc/MainBench.scala +++ b/src/compiler/scala/tools/nsc/MainBench.scala @@ -24,7 +24,7 @@ object MainBench extends Driver with EvalLoop { var start = System.nanoTime() for (i <- 0 until NIter) { if (i == NIter-1) { - theCompiler.settings.Ystatistics.value = true + theCompiler.settings.YstatisticsPhases.value = theCompiler.settings.YstatisticsPhases.default.get Statistics.enabled = true } process(args) diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 8df3969c49..351eb23c4c 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -283,7 +283,7 @@ abstract class Inliners extends SubComponent { } val tfa = new analysis.MTFAGrowable() - tfa.stat = global.settings.Ystatistics.value + tfa.stat = global.settings.YstatisticsEnabled val staleOut = new mutable.ListBuffer[BasicBlock] val splicedBlocks = mutable.Set.empty[BasicBlock] val staleIn = mutable.Set.empty[BasicBlock] diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala index d0b8fd70ed..cd2cb183b3 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala @@ -29,7 +29,7 @@ trait AbsScalaSettings { def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]): IntSetting def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting - def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit])(helper: MultiChoiceSetting => String): MultiChoiceSetting + def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], descriptions: List[String], default: Option[List[String]]): MultiChoiceSetting def OutputSetting(outputDirs: OutputDirs, default: String): OutputSetting def PathSetting(name: String, descr: String, default: String): PathSetting def PhasesSetting(name: String, descr: String, default: String): PhasesSetting diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index f26192f88a..b9b9257d63 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -209,12 +209,11 @@ class MutableSettings(val errorFn: String => Unit) def BooleanSetting(name: String, descr: String) = add(new BooleanSetting(name, descr)) def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String) = add(new ChoiceSetting(name, helpArg, descr, choices, default)) - def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = add(new IntSetting(name, descr, default, range, parser)) + def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = + add(new IntSetting(name, descr, default, range, parser)) def MultiStringSetting(name: String, arg: String, descr: String) = add(new MultiStringSetting(name, arg, descr)) - def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit] = None)( - helper: MultiChoiceSetting => String = _ => choices.mkString(f"$descr:%n", f"%n ", f"%n") - ) = - add(new MultiChoiceSetting(name, helpArg, descr, choices, default, helper)) + def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], descriptions: List[String], default: Option[List[String]] = None) = + add(new MultiChoiceSetting(name, helpArg, descr, choices, descriptions, default)) def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default)) def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default)) def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default)) @@ -553,57 +552,132 @@ class MutableSettings(val errorFn: String => Unit) } } - /** A setting that receives any combination of enumerated values, - * including "_" to mean all values and "help" for verbose info. - * In non-colonated mode, stops consuming args at the first - * non-value, instead of at the next option, as for a multi-string. + /** + * A setting that receives multiple values. There are two modes + * - choosing: the setting has a list of allowed chioces. + * - These choices can be positive or negative, for exmample "-option:positive,-negative" + * - If an option is both enabled positively and negatively, the positive wins + * - The choice "_" enables all choices that have not explicitly been disabled + * + * - !choosing: the setting accumulates all arguments. + * + * Arguments can be provided in colonated or non-colonated mode, i.e. "-option a b" or + * "-option:a,b". Note that arguments starting with a "-" can only be provided in colonated mode, + * otherwise they are interpreted as a new option. + * + * In colonated and choosing mode, the setting stops consuming arguments at the first non-choice, + * i.e. "-option a b c" only consumes "a" and "b" if "c" is not a valid choice. + * + * @param name command-line setting name, eg "-Xlint" + * @param arg description of the kind of arguments that need to be passed, eg "warning" + * @param descr description of the setting + * @param choices a list of allowed arguments. if empty, accepts any argument that doesn't start with "-" + * @param descriptions a description for each choice (for the help message), may be empty if no descriptions are necessary + * @param default default arguments, if none are provided */ class MultiChoiceSetting private[nsc]( name: String, arg: String, descr: String, override val choices: List[String], - val default: Option[() => Unit], - helper: MultiChoiceSetting => String + val descriptions: List[String], + val default: Option[List[String]] ) extends MultiStringSetting(name, s"_,$arg,-$arg", s"$descr: `_' for all, `$name:help' to list") { private def badChoice(s: String, n: String) = errorFn(s"'$s' is not a valid choice for '$name'") - private def choosing = choices.nonEmpty + private def choosing = choices.nonEmpty // choices are known, error on invalid args private def isChoice(s: String) = (s == "_") || (choices contains (s stripPrefix "-")) private var sawHelp = false private var sawAll = false - private val adderAll = () => sawAll = true - private val noargs = () => errorFn(s"'$name' requires an option. See '$name:help'.") + + private def pos(s: String) = s stripPrefix "-" + + /** + * This override takes care of including all possible choices in case the "_" argument was + * passed. It adds the choices that have not explicitly been enabled or disabled. This ensures + * that "this contains x" is consistent with "this.value contains x". + * + * The explicitly enabled / disabled options are stored in the underlying value [[v]], disabled + * ones are stored as "-arg". + * + * The fact that value and v are not the same leaks in some cases. For example, you should never + * use "value += arg", because that expands to "value = value + arg". In case sawAll is true, + * this would propagate all choices enabled by sawAll into the underlying v. + * + * Instead of "value += arg", you should use "this.add(arg)". I haven't found a good way to + * enforce that. + */ + override def value: List[String] = { + if (!sawAll) v + else { + // add only those choices which have not explicitly been enabled or disabled. + val fromAll = choices.filterNot(c => v.exists(pos(_) == c)) + v ++ fromAll + } + } + + /** + * Add an argument to the list of values. + */ + def add(arg: String) = { + if (choosing && !isChoice(arg)) badChoice(arg, name) + if (arg == "_") { + sawAll = true + value = v // the value_= setter has side effects, make sure to execute them. + } else { + val posArg = pos(arg) + if (arg == posArg) { + // positive overrides existing negative. so we filter existing (pos or neg), and add the arg + value = v.filterNot(pos(_) == posArg) :+ arg + } else { + // negative arg is only added if the arg doesn't exist yet (pos or neg) + if (!v.exists(pos(_) == posArg)) value = v :+ arg // not value += arg, see doc of the "value" getter + } + } + } override protected def tts(args: List[String], halting: Boolean) = { val added = collection.mutable.ListBuffer.empty[String] + def tryArg(arg: String) = arg match { - case "_" if choosing => addAll() case "help" if choosing => sawHelp = true - case s if !choosing || isChoice(s) => added += s + case s if !choosing || isChoice(s) => added += s // this case also adds "_" case s => badChoice(s, name) } + + // if "halting" is true, the args were not specified as , separated after a : + // but space separated after a space. we stop consuming args when seeing + // - an arg starting with a "-", that is a new option + // - if the choices are known (choosing), on the first non-choice def stoppingAt(arg: String) = (arg startsWith "-") || (choosing && !isChoice(arg)) + def loop(args: List[String]): List[String] = args match { case arg :: _ if halting && stoppingAt(arg) => args case arg :: rest => tryArg(arg) ; loop(rest) case Nil => Nil } + val rest = loop(args) - if (rest.size == args.size) - (default getOrElse noargs)() // if no arg consumed, trigger default action or error - else - value ++= added.toList // update all new settings at once + if (rest.size == args.size) default match { // if no arg consumed, use defaults or error + case Some(defaults) => defaults foreach add + case None => errorFn(s"'$name' requires an option. See '$name:help'.") + } else { + added.toList foreach add + } + Some(rest) } def isHelping: Boolean = sawHelp - def help: String = helper(this) - def addAll(): Unit = (default getOrElse adderAll)() - // the semantics is: s is enabled, i.e., either s or (_ but not -s) - override def contains(s: String) = isChoice(s) && (value contains s) || (sawAll && !(value contains s"-$s")) + def help: String = { + val choiceLength = choices.map(_.length).max + 1 + val formatStr = s" %-${choiceLength}s %s" + choices.zipAll(descriptions, "", "").map { + case (arg, descr) => formatStr.format(arg, descr) + } mkString (f"$descr%n", f"%n", "") + } } /** A setting that accumulates all strings supplied to it, diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 8e69598614..1609aa5ff7 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -84,13 +84,9 @@ trait ScalaSettings extends AbsScalaSettings name = "-language", helpArg = "feature", descr = description, - choices = features map (_._1) - ) { s => - val helpline: ((String, String)) => String = { - case (name, text) => f" $name%-25s $text%n" - } - features map helpline mkString (f"$description:%n", "", f"%n") - } + choices = features map (_._1), + descriptions = features map (_._2) + ) } /* @@ -193,7 +189,6 @@ trait ScalaSettings extends AbsScalaSettings val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "") val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") - val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _) val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") @@ -217,6 +212,20 @@ trait ScalaSettings extends AbsScalaSettings private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." + val YstatisticsPhases = { + val phases = List("parser", "typer", "patmat", "erasure", "cleanup") + val description = "Print compiler statistics for specific phases" + MultiChoiceSetting( + name = "-Ystatistics", + helpArg = "phase", + descr = description, + choices = phases, + descriptions = Nil, + default = Some(List("_"))) withPostSetHook { _ => scala.reflect.internal.util.Statistics.enabled = true } + } + + def YstatisticsEnabled = YstatisticsPhases.value.nonEmpty + /** Area-specific debug output. */ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 4c37633301..771543ec77 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -17,145 +17,93 @@ trait Warnings { // Warning semantics. val fatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.") - // These additional warnings are all so noisy as to be useless in their - // present form, but have the potential to offer useful info. - protected def allWarnings = lintWarnings ++ List( - warnDeadCode, - warnValueDiscard, - warnNumericWiden, - warnUnused, // SI-7712, SI-7707 warnUnused not quite ready for prime-time - warnUnusedImport, // currently considered too noisy for general use - warnValueOverrides // currently turned off as experimental - ) - // These warnings should be pretty quiet unless you're doing - // something inadvisable. - protected def lintWarnings = List( - warnInaccessible, - warnNullaryOverride, - warnNullaryUnit, - warnAdaptedArgs, - warnInferAny, - warnMissingInterpolator, - warnDocDetached, - warnPrivateShadow, - warnTypeParameterShadow, - warnPolyImplicitOverload, - warnOptionImplicit, - warnDelayedInit, - warnByNameRightAssociative, - warnPackageObjectClasses, - warnUnsoundMatch - ) - - // Individual warnings. They can be set with -Ywarn. - private def nonlintflag(name: String, text: String): BooleanSetting = BooleanSetting(name, text) - - val warnDeadCode = nonlintflag("-Ywarn-dead-code", - "Warn when dead code is identified.") - val warnValueDiscard = nonlintflag("-Ywarn-value-discard", - "Warn when non-Unit expression results are unused.") - val warnNumericWiden = nonlintflag("-Ywarn-numeric-widen", - "Warn when numerics are widened.") - val warnUnused = nonlintflag("-Ywarn-unused", - "Warn when local and private vals, vars, defs, and types are are unused") - val warnUnusedImport = nonlintflag("-Ywarn-unused-import", - "Warn when imports are unused") - - // Lint warnings that have no -Y avatar, created with new instead of the autoregistering factory method. - // They evaluate true if set to true or else are unset but -Xlint is true - private def lintflag(name: String, text: String): BooleanSetting = - new BooleanSetting(name, text) { - override def value = if (isSetByUser) super.value else xlint - } + // Non-lint warnings - val warnAdaptedArgs = lintflag("adapted-args", - "Warn if an argument list is modified to match the receiver.") - val warnNullaryUnit = lintflag("nullary-unit", - "Warn when nullary methods return Unit.") - val warnInaccessible = lintflag("inaccessible", - "Warn about inaccessible types in method signatures.") - val warnNullaryOverride = lintflag("nullary-override", - "Warn when non-nullary `def f()' overrides nullary `def f'.") - val warnInferAny = lintflag("infer-any", - "Warn when a type argument is inferred to be `Any`.") - val warnMissingInterpolator = lintflag("missing-interpolator", - "A string literal appears to be missing an interpolator id.") - val warnDocDetached = lintflag("doc-detached", - "A ScalaDoc comment appears to be detached from its element.") - val warnPrivateShadow = lintflag("private-shadow", - "A private field (or class parameter) shadows a superclass field.") - val warnTypeParameterShadow = lintflag("type-parameter-shadow", - "A local type parameter shadows a type already in scope.") - val warnPolyImplicitOverload = lintflag("poly-implicit-overload", - "Parameterized overloaded implicit methods are not visible as view bounds") - val warnOptionImplicit = lintflag("option-implicit", - "Option.apply used implicit view.") - val warnDelayedInit = lintflag("delayedinit-select", - "Selecting member of DelayedInit") - val warnByNameRightAssociative = lintflag("by-name-right-associative", - "By-name parameter of right associative operator") - val warnPackageObjectClasses = lintflag("package-object-classes", - "Class or object defined in package object") - val warnUnsoundMatch = lintflag("unsound-match", - "Pattern match may not be typesafe") + val warnDeadCode = BooleanSetting("-Ywarn-dead-code", "Warn when dead code is identified.") + val warnValueDiscard = BooleanSetting("-Ywarn-value-discard", "Warn when non-Unit expression results are unused.") + val warnNumericWiden = BooleanSetting("-Ywarn-numeric-widen", "Warn when numerics are widened.") + // SI-7712, SI-7707 warnUnused not quite ready for prime-time + val warnUnused = BooleanSetting("-Ywarn-unused", "Warn when local and private vals, vars, defs, and types are are unused.") + // currently considered too noisy for general use + val warnUnusedImport = BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.") // Experimental lint warnings that are turned off, but which could be turned on programmatically. // These warnings are said to blind those who dare enable them. // They are not activated by -Xlint and can't be enabled on the command line. - val warnValueOverrides = { - val flag = lintflag("value-overrides", "Generated value class method overrides an implementation") + val warnValueOverrides = { // currently turned off as experimental. creaded using constructor (new BS), so not available on the command line. + val flag = new BooleanSetting("value-overrides", "Generated value class method overrides an implementation") flag.value = false flag } - // The Xlint warning group. - private val xlint = new BooleanSetting("-Zunused", "True if -Xlint or -Xlint:_") - // On -Xlint or -Xlint:_, set xlint, otherwise set the lint warning unless already set true - val lint = { - val description = "Enable or disable specific warnings" - val choices = (lintWarnings map (_.name)).sorted - MultiChoiceSetting( - name = "-Xlint", - helpArg = "warning", - descr = description, - choices = choices, - default = Some(() => xlint.value = true) - ) { s => - def helpline(n: String) = lintWarnings.find(_.name == n).map(w => f" ${w.name}%-25s ${w.helpDescription}%n") - choices flatMap (helpline(_)) mkString (f"$description:%n", "", f"%n") - } withPostSetHook { x => - val Neg = "-" - def setPolitely(b: BooleanSetting, v: Boolean) = if (!b.isSetByUser || !b) b.value = v - def set(w: String, v: Boolean) = lintWarnings find (_.name == w) foreach (setPolitely(_, v)) - def propagate(ss: List[String]): Unit = ss match { - case w :: rest => if (w startsWith Neg) set(w stripPrefix Neg, false) else set(w, true) ; propagate(rest) - case Nil => () - } - propagate(x.value) - } - } + // Lint warnings + + def warnAdaptedArgs = lint contains "adapted-args" + def warnNullaryUnit = lint contains "nullary-unit" + def warnInaccessible = lint contains "inaccessible" + def warnNullaryOverride = lint contains "nullary-override" + def warnInferAny = lint contains "infer-any" + def warnMissingInterpolator = lint contains "missing-interpolator" + def warnDocDetached = lint contains "doc-detached" + def warnPrivateShadow = lint contains "private-shadow" + def warnTypeParameterShadow = lint contains "type-parameter-shadow" + def warnPolyImplicitOverload = lint contains "poly-implicit-overload" + def warnOptionImplicit = lint contains "option-implicit" + def warnDelayedInit = lint contains "delayedinit-select" + def warnByNameRightAssociative = lint contains "by-name-right-associative" + def warnPackageObjectClasses = lint contains "package-object-classes" + def warnUnsoundMatch = lint contains "unsound-match" // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") - val YwarnAdaptedArgs = BooleanSetting("-Ywarn-adapted-args", - "Warn if an argument list is modified to match the receiver.") enabling List(warnAdaptedArgs) - //withDeprecationMessage "Enable -Xlint:adapted-args" + def YwarnAdaptedArgs = warnAdaptedArgs @deprecated("Use warnNullaryUnit", since="2.11.2") - val YwarnNullaryUnit = BooleanSetting("-Ywarn-nullary-unit", - "Warn when nullary methods return Unit.") enabling List(warnNullaryUnit) - //withDeprecationMessage "Enable -Xlint:nullary-unit" + def YwarnNullaryUnit = warnNullaryUnit @deprecated("Use warnInaccessible", since="2.11.2") - val YwarnInaccessible = BooleanSetting("-Ywarn-inaccessible", - "Warn about inaccessible types in method signatures.") enabling List(warnInaccessible) - //withDeprecationMessage "Enable -Xlint:inaccessible" + def YwarnInaccessible = warnInaccessible @deprecated("Use warnNullaryOverride", since="2.11.2") - val YwarnNullaryOverride = BooleanSetting("-Ywarn-nullary-override", - "Warn when non-nullary `def f()' overrides nullary `def f'.") enabling List(warnNullaryOverride) - //withDeprecationMessage "Enable -Xlint:nullary-override" + def YwarnNullaryOverride = warnNullaryOverride @deprecated("Use warnInferAny", since="2.11.2") - val YwarnInferAny = BooleanSetting("-Ywarn-infer-any", - "Warn when a type argument is inferred to be `Any`.") enabling List(warnInferAny) - //withDeprecationMessage "Enable -Xlint:infer-any" + def YwarnInferAny = warnInferAny + + // The Xlint warning group. + val lint: MultiChoiceSetting = { + val description = "Enable or disable specific warnings" + + val choices = List( + ("adapted-args", "Warn if an argument list is modified to match the receiver.", true), + ("nullary-unit", "Warn when nullary methods return Unit.", true), + ("inaccessible", "Warn about inaccessible types in method signatures.", true), + ("nullary-override", "Warn when non-nullary `def f()' overrides nullary `def f'.", true), + ("infer-any", "Warn when a type argument is inferred to be `Any`.", true), + ("missing-interpolator", "A string literal appears to be missing an interpolator id.", false), + ("doc-detached", "A ScalaDoc comment appears to be detached from its element.", false), + ("private-shadow", "A private field (or class parameter) shadows a superclass field.", false), + ("type-parameter-shadow", "A local type parameter shadows a type already in scope.", false), + ("poly-implicit-overload", "Parameterized overloaded implicit methods are not visible as view bounds.", false), + ("option-implicit", "Option.apply used implicit view.", false), + ("delayedinit-select", "Selecting member of DelayedInit", false), + ("by-name-right-associative", "By-name parameter of right associative operator.", false), + ("package-object-classes", "Class or object defined in package object.", false), + ("unsound-match", "Pattern match may not be typesafe.", false) + ).sorted + + for (c <- choices.filter(_._3)) { + BooleanSetting("-Ywarn-"+ c._1, c._2) withPostSetHook { s => + if (s) lint.add(c._1) + else lint.add("-" + c._1) + } // withDeprecationMessage s"Enable -Xlint:${c._1}" + } + + MultiChoiceSetting( + name = "-Xlint", + helpArg = "warning", + descr = description, + choices = choices map (_._1), + descriptions = choices map (_._2), + default = Some(List("_")) + ) + } private lazy val warnSelectNullable = BooleanSetting("-Xcheck-null", "This option is obsolete and does nothing.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index fb7651ffd6..421021163e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -552,7 +552,7 @@ trait Infer extends Checkable { } case _ => context.tree.pos } - if (settings.warnInferAny.value && context.reportErrors && canWarnAboutAny) { + if (settings.warnInferAny && context.reportErrors && canWarnAboutAny) { foreachWithIndex(targs) ((targ, idx) => targ.typeSymbol match { case sym @ (AnyClass | AnyValClass) => diff --git a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala index 225f6ca68e..a5d579dc37 100644 --- a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala +++ b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala @@ -14,12 +14,10 @@ abstract class StatisticsInfo { import global._ import scala.reflect.internal.TreesStats.nodeByType - val phasesShown = List("parser", "typer", "patmat", "erasure", "cleanup") - val retainedCount = Statistics.newCounter("#retained tree nodes") val retainedByType = Statistics.newByClass("#retained tree nodes by type")(Statistics.newCounter("")) - def print(phase: Phase) = if (phasesShown contains phase.name) { + def print(phase: Phase) = if (settings.YstatisticsPhases contains phase.name) { inform("*** Cumulative statistics at phase " + phase) retainedCount.value = 0 for (c <- retainedByType.keys) diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index 960d7f8ac1..a0e015be32 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -39,7 +39,7 @@ class SettingsTest { } // for the given args, select the desired setting - private def check(args: String*)(b: MutableSettings => MutableSettings#BooleanSetting): MutableSettings#BooleanSetting = { + private def check(args: String*)(b: MutableSettings => Boolean): Boolean = { val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) val (ok, residual) = s.processArguments(args.toList, processAll = true) assert(residual.isEmpty) @@ -54,7 +54,68 @@ class SettingsTest { @Test def anonymousLintersCanBeNamed() { assertTrue(check("-Xlint")(_.warnMissingInterpolator)) // among Xlint assertFalse(check("-Xlint:-missing-interpolator")(_.warnMissingInterpolator)) + + // positive overrides negative, but not the other way around + assertTrue(check("-Xlint:-missing-interpolator,missing-interpolator")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:-missing-interpolator", "-Xlint:missing-interpolator")(_.warnMissingInterpolator)) + + assertTrue(check("-Xlint:missing-interpolator,-missing-interpolator")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:missing-interpolator", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator)) + + // -Xlint:_ adds all possible choices, but explicit negative settings will override + assertFalse(check("-Xlint:-missing-interpolator,_")(_.warnMissingInterpolator)) + assertFalse(check("-Xlint:-missing-interpolator", "-Xlint:_")(_.warnMissingInterpolator)) + assertFalse(check("-Xlint:_", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator)) + assertFalse(check("-Xlint:_,-missing-interpolator")(_.warnMissingInterpolator)) + + // -Xlint is the same as -Xlint:_ assertFalse(check("-Xlint:-missing-interpolator", "-Xlint")(_.warnMissingInterpolator)) assertFalse(check("-Xlint", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator)) + + // combination of positive, negative and _ + assertTrue(check("-Xlint:_,-missing-interpolator,missing-interpolator")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:-missing-interpolator,_,missing-interpolator")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:-missing-interpolator,missing-interpolator,_")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:missing-interpolator,-missing-interpolator,_")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint:missing-interpolator,_,-missing-interpolator")(_.warnMissingInterpolator)) + } + + @Test def xLintInvalidChoices(): Unit = { + assertThrows[IllegalArgumentException](check("-Xlint:-_")(_.warnAdaptedArgs)) + assertThrows[IllegalArgumentException](check("-Xlint:-warn-adapted-args")(_.warnAdaptedArgs)) // "warn-" should not be there + } + + @Test def xLintNonColonated(): Unit = { + assertTrue(check("-Xlint", "adapted-args", "-deprecation")(_.warnAdaptedArgs)) + assertFalse(check("-Xlint", "adapted-args", "-deprecation")(_.warnMissingInterpolator)) + assertTrue(check("-Xlint", "adapted-args", "missing-interpolator", "-deprecation")(s => s.warnMissingInterpolator && s.warnAdaptedArgs)) + assertThrows[IllegalArgumentException](check("-Xlint", "adapted-args", "-missing-interpolator")(_.warnAdaptedArgs)) // non-colonated: cannot provide negative args + } + + @Test def xLintContainsValues(): Unit = { + // make sure that lint.contains and lint.value.contains are consistent + def t(s: MutableSettings, v: String) = { + val r = s.lint.contains(v) + assertSame(r, s.lint.value.contains(v)) + r + } + + assertTrue(check("-Xlint")(t(_, "adapted-args"))) + assertTrue(check("-Xlint:_")(t(_, "adapted-args"))) + assertFalse(check("-Xlint:_,-adapted-args")(t(_, "adapted-args"))) + assertFalse(check("-Xlint:-adapted-args,_")(t(_, "adapted-args"))) + assertTrue(check("-Xlint:-adapted-args,_,adapted-args")(t(_, "adapted-args"))) + } + + @Test def xLintDeprecatedAlias(): Unit = { + assertTrue(check("-Ywarn-adapted-args")(_.warnAdaptedArgs)) + assertTrue(check("-Xlint:_,-adapted-args", "-Ywarn-adapted-args")(_.warnAdaptedArgs)) + assertTrue(check("-Xlint:-adapted-args", "-Ywarn-adapted-args")(_.warnAdaptedArgs)) + assertTrue(check("-Ywarn-adapted-args", "-Xlint:-adapted-args,_")(_.warnAdaptedArgs)) + + assertFalse(check("-Ywarn-adapted-args:false")(_.warnAdaptedArgs)) + assertFalse(check("-Ywarn-adapted-args:false", "-Xlint:_")(_.warnAdaptedArgs)) + assertFalse(check("-Ywarn-adapted-args:false", "-Xlint:_,-adapted-args")(_.warnAdaptedArgs)) + assertTrue(check("-Ywarn-adapted-args:false", "-Xlint:_,adapted-args")(_.warnAdaptedArgs)) } } -- cgit v1.2.3 From 7655a70489f565a5a7a165f893b4a1e44c3cb2b8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 25 Aug 2014 10:15:04 -0700 Subject: Use Enumeration for MultiChoiceSetting This is pretty easy, since a ValueSet is a BitSet. When the setting is updated, recompute the current set of values, which is cheap and succinct. Checking a flag is also easy and fast. Choices in MultiChoiceSettings may enable other choices. --- src/compiler/scala/tools/nsc/CompilerCommand.scala | 2 +- src/compiler/scala/tools/nsc/MainBench.scala | 2 +- .../tools/nsc/settings/AbsScalaSettings.scala | 24 +- .../scala/tools/nsc/settings/MutableSettings.scala | 241 +++++++++++++-------- .../scala/tools/nsc/settings/ScalaSettings.scala | 39 ++-- .../scala/tools/nsc/settings/Warnings.scala | 100 +++++---- .../scala/tools/nsc/util/StatisticsInfo.scala | 2 +- .../scala/tools/nsc/settings/SettingsTest.scala | 46 +++- 8 files changed, 288 insertions(+), 168 deletions(-) diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 3ded456378..9b8e9fa330 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -107,7 +107,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { else { val sb = new StringBuilder allSettings foreach { - case s: MultiChoiceSetting if s.isHelping => sb append s.help + case s: MultiChoiceSetting[_] if s.isHelping => sb append s.help case _ => } sb.toString diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala index eb56c8a230..f01de0cbe1 100644 --- a/src/compiler/scala/tools/nsc/MainBench.scala +++ b/src/compiler/scala/tools/nsc/MainBench.scala @@ -24,7 +24,7 @@ object MainBench extends Driver with EvalLoop { var start = System.nanoTime() for (i <- 0 until NIter) { if (i == NIter-1) { - theCompiler.settings.YstatisticsPhases.value = theCompiler.settings.YstatisticsPhases.default.get + theCompiler.settings.Ystatistics.default.get foreach theCompiler.settings.Ystatistics.add Statistics.enabled = true } process(args) diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala index cd2cb183b3..6b339b2a6d 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala @@ -7,20 +7,24 @@ package scala package tools.nsc package settings +import scala.language.higherKinds + trait AbsScalaSettings { self: AbsSettings => + type MultiChoiceEnumeration <: Enumeration + type Setting <: AbsSetting - type BooleanSetting <: Setting { type T = Boolean } - type ChoiceSetting <: Setting { type T = String } - type IntSetting <: Setting { type T = Int } - type MultiStringSetting <: Setting { type T = List[String] } - type MultiChoiceSetting <: Setting { type T = List[String] } - type PathSetting <: Setting { type T = String } - type PhasesSetting <: Setting { type T = List[String] } - type StringSetting <: Setting { type T = String } - type PrefixSetting <: Setting { type T = List[String] } + type BooleanSetting <: Setting { type T = Boolean } + type ChoiceSetting <: Setting { type T = String } + type IntSetting <: Setting { type T = Int } + type MultiStringSetting <: Setting { type T = List[String] } + type MultiChoiceSetting[E <: MultiChoiceEnumeration] <: Setting { type T <: E#ValueSet } + type PathSetting <: Setting { type T = String } + type PhasesSetting <: Setting { type T = List[String] } + type StringSetting <: Setting { type T = String } + type PrefixSetting <: Setting { type T = List[String] } type OutputDirs type OutputSetting <: Setting @@ -29,7 +33,7 @@ trait AbsScalaSettings { def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]): IntSetting def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting - def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], descriptions: List[String], default: Option[List[String]]): MultiChoiceSetting + def MultiChoiceSetting[E <: MultiChoiceEnumeration](name: String, helpArg: String, descr: String, domain: E, default: Option[List[String]]): MultiChoiceSetting[E] def OutputSetting(outputDirs: OutputDirs, default: String): OutputSetting def PathSetting(name: String, descr: String, default: String): PathSetting def PhasesSetting(name: String, descr: String, default: String): PhasesSetting diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index b9b9257d63..78d7061066 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -212,8 +212,8 @@ class MutableSettings(val errorFn: String => Unit) def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = add(new IntSetting(name, descr, default, range, parser)) def MultiStringSetting(name: String, arg: String, descr: String) = add(new MultiStringSetting(name, arg, descr)) - def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], descriptions: List[String], default: Option[List[String]] = None) = - add(new MultiChoiceSetting(name, helpArg, descr, choices, descriptions, default)) + def MultiChoiceSetting[E <: MultiChoiceEnumeration](name: String, helpArg: String, descr: String, domain: E, default: Option[List[String]] = None) = + add(new MultiChoiceSetting[E](name, helpArg, descr, domain, default)) def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default)) def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default)) def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default)) @@ -366,7 +366,7 @@ class MutableSettings(val errorFn: String => Unit) def withDeprecationMessage(msg: String): this.type = { _deprecationMessage = Some(msg) ; this } } - /** A setting represented by an integer */ + /** A setting represented by an integer. */ class IntSetting private[nsc]( name: String, descr: String, @@ -553,122 +553,179 @@ class MutableSettings(val errorFn: String => Unit) } /** - * A setting that receives multiple values. There are two modes - * - choosing: the setting has a list of allowed chioces. - * - These choices can be positive or negative, for exmample "-option:positive,-negative" - * - If an option is both enabled positively and negatively, the positive wins - * - The choice "_" enables all choices that have not explicitly been disabled + * Each [[MultiChoiceSetting]] takes a MultiChoiceEnumeration as domain. The enumartion may + * use the Choice class to define values, or simply use the default `Value` constructor: * - * - !choosing: the setting accumulates all arguments. + * object SettingDomain extends MultiChoiceEnumeration { val arg1, arg2 = Value } * - * Arguments can be provided in colonated or non-colonated mode, i.e. "-option a b" or - * "-option:a,b". Note that arguments starting with a "-" can only be provided in colonated mode, - * otherwise they are interpreted as a new option. + * Or * - * In colonated and choosing mode, the setting stops consuming arguments at the first non-choice, - * i.e. "-option a b c" only consumes "a" and "b" if "c" is not a valid choice. + * object SettingDomain extends MultiChoiceEnumeration { + * val arg1 = Choice("arg1", "help") + * val arg2 = Choice("arg2", "help") + * } * - * @param name command-line setting name, eg "-Xlint" - * @param arg description of the kind of arguments that need to be passed, eg "warning" - * @param descr description of the setting - * @param choices a list of allowed arguments. if empty, accepts any argument that doesn't start with "-" - * @param descriptions a description for each choice (for the help message), may be empty if no descriptions are necessary - * @param default default arguments, if none are provided + * Choices with a non-empty `expandsTo` enable other options. Note that expanding choices are + * not present in the multiChoiceSetting.value set, only their expansion. */ - class MultiChoiceSetting private[nsc]( + abstract class MultiChoiceEnumeration extends Enumeration { + case class Choice(name: String, help: String = "", expandsTo: List[Choice] = Nil) extends Val(name) + } + + /** A Setting that collects string-valued settings from an enumerated domain. + * - These choices can be turned on or off: "-option:on,-off" + * - If an option is set both on and off, then the option is on + * - The choice "_" enables all choices that have not been explicitly disabled + * + * Arguments can be provided in colonated or non-colonated mode, i.e. "-option a b" or + * "-option:a,b". Note that arguments starting with a "-" can only be provided in colonated mode, + * otherwise they are interpreted as a new option. + * + * In non-colonated mode, the setting stops consuming arguments at the first non-choice, + * i.e. "-option a b c" only consumes "a" and "b" if "c" is not a valid choice. + * + * @param name command-line setting name, eg "-Xlint" + * @param helpArg help description for the kind of arguments it takes, eg "warning" + * @param descr description of the setting + * @param domain enumeration of choices implementing MultiChoice, or the string value is + * taken for the name + * @param default If Some(args), the default options if none are provided. If None, an + * error is printed if there are no arguments. + */ + class MultiChoiceSetting[E <: MultiChoiceEnumeration] private[nsc]( name: String, - arg: String, + helpArg: String, descr: String, - override val choices: List[String], - val descriptions: List[String], + val domain: E, val default: Option[List[String]] - ) extends MultiStringSetting(name, s"_,$arg,-$arg", s"$descr: `_' for all, `$name:help' to list") { + ) extends Setting(name, s"$descr: `_' for all, `$name:help' to list") with Clearable { + + withHelpSyntax(s"$name:<_,$helpArg,-$helpArg>") - private def badChoice(s: String, n: String) = errorFn(s"'$s' is not a valid choice for '$name'") - private def choosing = choices.nonEmpty // choices are known, error on invalid args - private def isChoice(s: String) = (s == "_") || (choices contains (s stripPrefix "-")) + object ChoiceOrVal { + def unapply(a: Any): Option[(String, String, List[domain.Choice])] = a match { + case c: domain.Choice => Some((c.name, c.help, c.expandsTo)) + case v: domain.Value => Some((v.toString, "", Nil)) + case _ => None + } + } - private var sawHelp = false - private var sawAll = false + type T = domain.ValueSet + protected var v: T = domain.ValueSet.empty + + // Explicitly enabled or disabled. Yeas may contain expanding options, nays may not. + private var yeas = domain.ValueSet.empty + private var nays = domain.ValueSet.empty + + // Asked for help + private var sawHelp = false + // Wildcard _ encountered + private var sawAll = false + + private def badChoice(s: String) = errorFn(s"'$s' is not a valid choice for '$name'") + private def isChoice(s: String) = (s == "_") || (choices contains pos(s)) private def pos(s: String) = s stripPrefix "-" + private def isPos(s: String) = !(s startsWith "-") - /** - * This override takes care of including all possible choices in case the "_" argument was - * passed. It adds the choices that have not explicitly been enabled or disabled. This ensures - * that "this contains x" is consistent with "this.value contains x". - * - * The explicitly enabled / disabled options are stored in the underlying value [[v]], disabled - * ones are stored as "-arg". - * - * The fact that value and v are not the same leaks in some cases. For example, you should never - * use "value += arg", because that expands to "value = value + arg". In case sawAll is true, - * this would propagate all choices enabled by sawAll into the underlying v. - * - * Instead of "value += arg", you should use "this.add(arg)". I haven't found a good way to - * enforce that. - */ - override def value: List[String] = { - if (!sawAll) v - else { - // add only those choices which have not explicitly been enabled or disabled. - val fromAll = choices.filterNot(c => v.exists(pos(_) == c)) - v ++ fromAll + override val choices: List[String] = domain.values.toList map { + case ChoiceOrVal(name, _, _) => name + } + + def descriptions: List[String] = domain.values.toList map { + case ChoiceOrVal(_, "", x :: xs) => "Enables the options "+ (x :: xs).map(_.name).mkString(", ") + case ChoiceOrVal(_, descr, _) => descr + case _ => "" + } + + /** (Re)compute from current yeas, nays, wildcard status. */ + def compute() = { + def nonExpanding(vs: domain.ValueSet) = vs filter { + case ChoiceOrVal(_, _, others) => others.isEmpty + } + + /** + * Expand an option, if necessary recursively. Expanding options are not included in the + * result (consistent with "_", which is not in the values either). Explicitly excluded + * options (in nays) are not added. + * + * Note: by precondition, options in nays are not expanding, they can only be leaves. + */ + def expand(vs: domain.ValueSet): domain.ValueSet = vs flatMap { + // expand + case c @ ChoiceOrVal(_, _, others) if others.nonEmpty => expand(domain.ValueSet(others: _*)) + case c @ ChoiceOrVal(_, _, _) if !nays(c) => domain.ValueSet(c) + case _ => domain.ValueSet.empty } + val expandedYeas = expand(yeas) + + + // we include everything, except those explicitly disabled. + val expandedAll = if (sawAll) nonExpanding(domain.values) &~ nays + else domain.ValueSet.empty + + value = nonExpanding(yeas) | expandedYeas | expandedAll } - /** - * Add an argument to the list of values. - */ - def add(arg: String) = { - if (choosing && !isChoice(arg)) badChoice(arg, name) - if (arg == "_") { + /** Add a named choice to the multichoice value. */ + def add(arg: String) = arg match { + case _ if !isChoice(arg) => + badChoice(arg) + case "_" => sawAll = true - value = v // the value_= setter has side effects, make sure to execute them. - } else { - val posArg = pos(arg) - if (arg == posArg) { - // positive overrides existing negative. so we filter existing (pos or neg), and add the arg - value = v.filterNot(pos(_) == posArg) :+ arg - } else { - // negative arg is only added if the arg doesn't exist yet (pos or neg) - if (!v.exists(pos(_) == posArg)) value = v :+ arg // not value += arg, see doc of the "value" getter + compute() + case _ if isPos(arg) => + yeas += domain withName arg + compute() + case _ => + val choice = domain withName pos(arg) + choice match { + case ChoiceOrVal(_, _, _ :: _) => errorFn(s"'${pos(arg)}' cannot be negated, it enables other arguments") + case _ => } - } + nays += choice + compute() } - override protected def tts(args: List[String], halting: Boolean) = { + def tryToSet(args: List[String]) = tryToSetArgs(args, halting = true) + override def tryToSetColon(args: List[String]) = tryToSetArgs(args, halting = false) + override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide + + /** Try to set args, handling "help" and default. + * The "halting" parameter means args were "-option a b c -else" so halt + * on "-else" or other non-choice. Otherwise, args were "-option:a,b,c,d", + * so process all and report non-choices as errors. + * @param args args to process + * @param halting stop on non-arg + */ + private def tryToSetArgs(args: List[String], halting: Boolean) = { val added = collection.mutable.ListBuffer.empty[String] def tryArg(arg: String) = arg match { - case "help" if choosing => sawHelp = true - case s if !choosing || isChoice(s) => added += s // this case also adds "_" - case s => badChoice(s, name) + case "help" => sawHelp = true + case s if isChoice(s) => added += s // this case also adds "_" + case s => badChoice(s) } - - // if "halting" is true, the args were not specified as , separated after a : - // but space separated after a space. we stop consuming args when seeing - // - an arg starting with a "-", that is a new option - // - if the choices are known (choosing), on the first non-choice - def stoppingAt(arg: String) = (arg startsWith "-") || (choosing && !isChoice(arg)) - def loop(args: List[String]): List[String] = args match { - case arg :: _ if halting && stoppingAt(arg) => args - case arg :: rest => tryArg(arg) ; loop(rest) - case Nil => Nil + case arg :: _ if halting && (!isPos(arg) || !isChoice(arg)) => args + case arg :: rest => tryArg(arg) ; loop(rest) + case Nil => Nil } - val rest = loop(args) - if (rest.size == args.size) default match { // if no arg consumed, use defaults or error + + // if no arg consumed, use defaults or error; otherwise, add what they added + if (rest.size == args.size) default match { case Some(defaults) => defaults foreach add case None => errorFn(s"'$name' requires an option. See '$name:help'.") } else { - added.toList foreach add + added foreach add } Some(rest) } + def contains(choice: domain.Value): Boolean = value contains choice + def isHelping: Boolean = sawHelp def help: String = { @@ -678,6 +735,16 @@ class MutableSettings(val errorFn: String => Unit) case (arg, descr) => formatStr.format(arg, descr) } mkString (f"$descr%n", f"%n", "") } + + def clear(): Unit = { + v = domain.ValueSet.empty + yeas = domain.ValueSet.empty + nays = domain.ValueSet.empty + sawAll = false + sawHelp = false + } + def unparse: List[String] = value.toList map (s => s"$name:$s") + def contains(s: String) = value contains (domain withName s) } /** A setting that accumulates all strings supplied to it, @@ -693,15 +760,15 @@ class MutableSettings(val errorFn: String => Unit) def appendToValue(str: String) = value ++= List(str) // try to set. halting means halt at first non-arg - protected def tts(args: List[String], halting: Boolean) = { + protected def tryToSetArgs(args: List[String], halting: Boolean) = { def loop(args: List[String]): List[String] = args match { case arg :: rest => if (halting && (arg startsWith "-")) args else { appendToValue(arg) ; loop(rest) } case Nil => Nil } Some(loop(args)) } - def tryToSet(args: List[String]) = tts(args, halting = true) - override def tryToSetColon(args: List[String]) = tts(args, halting = false) + def tryToSet(args: List[String]) = tryToSetArgs(args, halting = true) + override def tryToSetColon(args: List[String]) = tryToSetArgs(args, halting = false) override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide def clear(): Unit = (v = Nil) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 1609aa5ff7..91b03869e5 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -45,7 +45,7 @@ trait ScalaSettings extends AbsScalaSettings def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) /** Any -multichoice:help? Nicer if any option could report that it had help to offer. */ - private def multihelp = allSettings exists { case s: MultiChoiceSetting => s.isHelping case _ => false } + private def multihelp = allSettings exists { case s: MultiChoiceSetting[_] => s.isHelping case _ => false } /** Is an info setting set? */ def isInfo = (infoSettings exists (_.isSetByUser)) || multihelp @@ -69,23 +69,22 @@ trait ScalaSettings extends AbsScalaSettings // Would be nice to build this dynamically from scala.languageFeature. // The two requirements: delay error checking until you have symbols, and let compiler command build option-specific help. + object languageFeatures extends MultiChoiceEnumeration { + val dynamics = Choice("dynamics", "Allow direct or indirect subclasses of scala.Dynamic") + val postfixOps = Choice("postfixOps", "Allow postfix operator notation, such as `1 to 10 toList'") + val reflectiveCalls = Choice("reflectiveCalls", "Allow reflective access to members of structural types") + val implicitConversions = Choice("implicitConversions", "Allow definition of implicit functions called views") + val higherKinds = Choice("higherKinds", "Allow higher-kinded types") + val existentials = Choice("existentials", "Existential types (besides wildcard types) can be written and inferred") + val macros = Choice("experimental.macros", "Allow macro defintion (besides implementation and application)") + } val language = { - val features = List( - "dynamics" -> "Allow direct or indirect subclasses of scala.Dynamic", - "postfixOps" -> "Allow postfix operator notation, such as `1 to 10 toList'", - "reflectiveCalls" -> "Allow reflective access to members of structural types", - "implicitConversions" -> "Allow definition of implicit functions called views", - "higherKinds" -> "Allow higher-kinded types", // "Ask Adriaan, but if you have to ask..." - "existentials" -> "Existential types (besides wildcard types) can be written and inferred", - "experimental.macros" -> "Allow macro defintion (besides implementation and application)" - ) val description = "Enable or disable language features" MultiChoiceSetting( name = "-language", helpArg = "feature", descr = description, - choices = features map (_._1), - descriptions = features map (_._2) + domain = languageFeatures ) } @@ -212,19 +211,19 @@ trait ScalaSettings extends AbsScalaSettings private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." - val YstatisticsPhases = { - val phases = List("parser", "typer", "patmat", "erasure", "cleanup") + object YstatisticsPhases extends MultiChoiceEnumeration { val parser, typer, patmat, erasure, cleanup = Value } + val Ystatistics = { val description = "Print compiler statistics for specific phases" MultiChoiceSetting( - name = "-Ystatistics", + name = "-Ystatistics", helpArg = "phase", - descr = description, - choices = phases, - descriptions = Nil, - default = Some(List("_"))) withPostSetHook { _ => scala.reflect.internal.util.Statistics.enabled = true } + descr = description, + domain = YstatisticsPhases, + default = Some(List("_")) + ) withPostSetHook { _ => scala.reflect.internal.util.Statistics.enabled = true } } - def YstatisticsEnabled = YstatisticsPhases.value.nonEmpty + def YstatisticsEnabled = Ystatistics.value.nonEmpty /** Area-specific debug output. */ diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 771543ec77..574825874d 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -38,21 +38,45 @@ trait Warnings { // Lint warnings - def warnAdaptedArgs = lint contains "adapted-args" - def warnNullaryUnit = lint contains "nullary-unit" - def warnInaccessible = lint contains "inaccessible" - def warnNullaryOverride = lint contains "nullary-override" - def warnInferAny = lint contains "infer-any" - def warnMissingInterpolator = lint contains "missing-interpolator" - def warnDocDetached = lint contains "doc-detached" - def warnPrivateShadow = lint contains "private-shadow" - def warnTypeParameterShadow = lint contains "type-parameter-shadow" - def warnPolyImplicitOverload = lint contains "poly-implicit-overload" - def warnOptionImplicit = lint contains "option-implicit" - def warnDelayedInit = lint contains "delayedinit-select" - def warnByNameRightAssociative = lint contains "by-name-right-associative" - def warnPackageObjectClasses = lint contains "package-object-classes" - def warnUnsoundMatch = lint contains "unsound-match" + object LintWarnings extends MultiChoiceEnumeration { + class LintWarning(name: String, help: String, val yAliased: Boolean) extends Choice(name, help) + def LintWarning(name: String, help: String, yAliased: Boolean = false) = new LintWarning(name, help, yAliased) + + val AdaptedArgs = LintWarning("adapted-args", "Warn if an argument list is modified to match the receiver.", true) + val NullaryUnit = LintWarning("nullary-unit", "Warn when nullary methods return Unit.", true) + val Inaccessible = LintWarning("inaccessible", "Warn about inaccessible types in method signatures.", true) + val NullaryOverride = LintWarning("nullary-override", "Warn when non-nullary `def f()' overrides nullary `def f'.", true) + val InferAny = LintWarning("infer-any", "Warn when a type argument is inferred to be `Any`.", true) + val MissingInterpolator = LintWarning("missing-interpolator", "A string literal appears to be missing an interpolator id.") + val DocDetached = LintWarning("doc-detached", "A ScalaDoc comment appears to be detached from its element.") + val PrivateShadow = LintWarning("private-shadow", "A private field (or class parameter) shadows a superclass field.") + val TypeParameterShadow = LintWarning("type-parameter-shadow", "A local type parameter shadows a type already in scope.") + val PolyImplicitOverload = LintWarning("poly-implicit-overload", "Parameterized overloaded implicit methods are not visible as view bounds.") + val OptionImplicit = LintWarning("option-implicit", "Option.apply used implicit view.") + val DelayedInitSelect = LintWarning("delayedinit-select", "Selecting member of DelayedInit") + val ByNameRightAssociative = LintWarning("by-name-right-associative", "By-name parameter of right associative operator.") + val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.") + val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.") + + def allWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] + } + import LintWarnings._ + + def warnAdaptedArgs = lint contains AdaptedArgs + def warnNullaryUnit = lint contains NullaryUnit + def warnInaccessible = lint contains Inaccessible + def warnNullaryOverride = lint contains NullaryOverride + def warnInferAny = lint contains InferAny + def warnMissingInterpolator = lint contains MissingInterpolator + def warnDocDetached = lint contains DocDetached + def warnPrivateShadow = lint contains PrivateShadow + def warnTypeParameterShadow = lint contains TypeParameterShadow + def warnPolyImplicitOverload = lint contains PolyImplicitOverload + def warnOptionImplicit = lint contains OptionImplicit + def warnDelayedInit = lint contains DelayedInitSelect + def warnByNameRightAssociative = lint contains ByNameRightAssociative + def warnPackageObjectClasses = lint contains PackageObjectClasses + def warnUnsoundMatch = lint contains UnsoundMatch // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") @@ -67,41 +91,23 @@ trait Warnings { def YwarnInferAny = warnInferAny // The Xlint warning group. - val lint: MultiChoiceSetting = { - val description = "Enable or disable specific warnings" - - val choices = List( - ("adapted-args", "Warn if an argument list is modified to match the receiver.", true), - ("nullary-unit", "Warn when nullary methods return Unit.", true), - ("inaccessible", "Warn about inaccessible types in method signatures.", true), - ("nullary-override", "Warn when non-nullary `def f()' overrides nullary `def f'.", true), - ("infer-any", "Warn when a type argument is inferred to be `Any`.", true), - ("missing-interpolator", "A string literal appears to be missing an interpolator id.", false), - ("doc-detached", "A ScalaDoc comment appears to be detached from its element.", false), - ("private-shadow", "A private field (or class parameter) shadows a superclass field.", false), - ("type-parameter-shadow", "A local type parameter shadows a type already in scope.", false), - ("poly-implicit-overload", "Parameterized overloaded implicit methods are not visible as view bounds.", false), - ("option-implicit", "Option.apply used implicit view.", false), - ("delayedinit-select", "Selecting member of DelayedInit", false), - ("by-name-right-associative", "By-name parameter of right associative operator.", false), - ("package-object-classes", "Class or object defined in package object.", false), - ("unsound-match", "Pattern match may not be typesafe.", false) - ).sorted - - for (c <- choices.filter(_._3)) { - BooleanSetting("-Ywarn-"+ c._1, c._2) withPostSetHook { s => - if (s) lint.add(c._1) - else lint.add("-" + c._1) - } // withDeprecationMessage s"Enable -Xlint:${c._1}" + val lint: MultiChoiceSetting[LintWarnings.type] = { + import LintWarnings._ + + allWarnings.sortBy(_.name) foreach { + case l: LintWarning if l.yAliased => + BooleanSetting(s"-Ywarn-${l.name}", {l.help}) withPostSetHook { s => + lint.add(if (s) l.name else s"-${l.name}") + } // withDeprecationMessage s"Enable -Xlint:${c._1}" + case _ => } MultiChoiceSetting( - name = "-Xlint", - helpArg = "warning", - descr = description, - choices = choices map (_._1), - descriptions = choices map (_._2), - default = Some(List("_")) + name = "-Xlint", + helpArg = "warning", + descr = "Enable or disable specific warnings", + domain = LintWarnings, + default = Some(List("_")) ) } diff --git a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala index a5d579dc37..be245347a8 100644 --- a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala +++ b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala @@ -17,7 +17,7 @@ abstract class StatisticsInfo { val retainedCount = Statistics.newCounter("#retained tree nodes") val retainedByType = Statistics.newByClass("#retained tree nodes by type")(Statistics.newCounter("")) - def print(phase: Phase) = if (settings.YstatisticsPhases contains phase.name) { + def print(phase: Phase) = if (settings.Ystatistics contains phase.name) { inform("*** Cumulative statistics at phase " + phase) retainedCount.value = 0 for (c <- retainedByType.keys) diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index a0e015be32..0b8029326e 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -96,7 +96,7 @@ class SettingsTest { // make sure that lint.contains and lint.value.contains are consistent def t(s: MutableSettings, v: String) = { val r = s.lint.contains(v) - assertSame(r, s.lint.value.contains(v)) + assertSame(r, s.lint.value.contains((s.LintWarnings withName v).asInstanceOf[s.lint.domain.Value])) r } @@ -118,4 +118,48 @@ class SettingsTest { assertFalse(check("-Ywarn-adapted-args:false", "-Xlint:_,-adapted-args")(_.warnAdaptedArgs)) assertTrue(check("-Ywarn-adapted-args:false", "-Xlint:_,adapted-args")(_.warnAdaptedArgs)) } + + @Test def expandingMultichoice(): Unit = { + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + object mChoices extends s.MultiChoiceEnumeration { + val a = Choice("a") + val b = Choice("b") + val c = Choice("c") + val d = Choice("d") + + val ab = Choice("ab", expandsTo = List(a, b)) + val ac = Choice("ac", expandsTo = List(a, c)) + val uber = Choice("uber", expandsTo = List(ab, d)) + } + val m = s.MultiChoiceSetting("-m", "args", "magic sauce", mChoices, Some(List("ac"))) + + def check(args: String*)(t: s.MultiChoiceSetting[mChoices.type] => Boolean): Boolean = { + m.clear() + val (ok, rest) = s.processArguments(args.toList, processAll = true) + assert(rest.isEmpty) + t(m) + } + + import mChoices._ + + assertTrue(check("-m")(_.value == Set(a,c))) + assertTrue(check("-m:a,-b,c")(_.value == Set(a,c))) + + // expanding options don't end up in the value set, only the terminal ones + assertTrue(check("-m:ab,ac")(_.value == Set(a,b,c))) + assertTrue(check("-m:_")(_.value == Set(a,b,c,d))) + assertTrue(check("-m:uber,ac")(_.value == Set(a,b,c,d))) // recursive expansion of uber + + // explicit nays + assertTrue(check("-m:_,-b")(_.value == Set(a,c,d))) + assertTrue(check("-m:b,_,-b")(_.value == Set(a,b,c,d))) + assertTrue(check("-m:ac,-c")(_.value == Set(a))) + assertTrue(check("-m:ac,-a,-c")(_.value == Set())) + assertTrue(check("-m:-d,ac")(_.value == Set(a,c))) + assertTrue(check("-m:-b,ac,uber")(_.value == Set(a,c,d))) + + assertThrows[IllegalArgumentException](check("-m:-_")(_ => true), _ contains "'-_' is not a valid choice") + assertThrows[IllegalArgumentException](check("-m:a,b,-ab")(_ => true), _ contains "'ab' cannot be negated") + assertThrows[IllegalArgumentException](check("-m:a,ac,-uber,uber")(_ => true), _ contains "'uber' cannot be negated") + } } -- cgit v1.2.3 From 861ad72b59c5ea7856b2853c092aaeb15453caea Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 3 Sep 2014 09:38:36 +0200 Subject: Address PR feedback, fix MultiChoiceSetting.contains --- .../scala/tools/nsc/settings/MutableSettings.scala | 7 +++-- .../scala/tools/nsc/settings/Warnings.scala | 33 +++++++++------------- .../scala/tools/nsc/settings/SettingsTest.scala | 2 ++ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 78d7061066..1f75afd49a 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -553,7 +553,7 @@ class MutableSettings(val errorFn: String => Unit) } /** - * Each [[MultiChoiceSetting]] takes a MultiChoiceEnumeration as domain. The enumartion may + * Each [[MultiChoiceSetting]] takes a MultiChoiceEnumeration as domain. The enumeration may * use the Choice class to define values, or simply use the default `Value` constructor: * * object SettingDomain extends MultiChoiceEnumeration { val arg1, arg2 = Value } @@ -572,7 +572,8 @@ class MutableSettings(val errorFn: String => Unit) case class Choice(name: String, help: String = "", expandsTo: List[Choice] = Nil) extends Val(name) } - /** A Setting that collects string-valued settings from an enumerated domain. + /** + * A Setting that collects string-valued settings from an enumerated domain. * - These choices can be turned on or off: "-option:on,-off" * - If an option is set both on and off, then the option is on * - The choice "_" enables all choices that have not been explicitly disabled @@ -744,7 +745,7 @@ class MutableSettings(val errorFn: String => Unit) sawHelp = false } def unparse: List[String] = value.toList map (s => s"$name:$s") - def contains(s: String) = value contains (domain withName s) + def contains(s: String) = domain.values.find(_.toString == s).exists(value.contains) } /** A setting that accumulates all strings supplied to it, diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 574825874d..c400e8c29c 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -58,7 +58,7 @@ trait Warnings { val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.") val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.") - def allWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] + def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } import LintWarnings._ @@ -91,24 +91,19 @@ trait Warnings { def YwarnInferAny = warnInferAny // The Xlint warning group. - val lint: MultiChoiceSetting[LintWarnings.type] = { - import LintWarnings._ - - allWarnings.sortBy(_.name) foreach { - case l: LintWarning if l.yAliased => - BooleanSetting(s"-Ywarn-${l.name}", {l.help}) withPostSetHook { s => - lint.add(if (s) l.name else s"-${l.name}") - } // withDeprecationMessage s"Enable -Xlint:${c._1}" - case _ => - } - - MultiChoiceSetting( - name = "-Xlint", - helpArg = "warning", - descr = "Enable or disable specific warnings", - domain = LintWarnings, - default = Some(List("_")) - ) + val lint = MultiChoiceSetting( + name = "-Xlint", + helpArg = "warning", + descr = "Enable or disable specific warnings", + domain = LintWarnings, + default = Some(List("_"))) + + allLintWarnings foreach { + case w if w.yAliased => + BooleanSetting(s"-Ywarn-${w.name}", {w.help}) withPostSetHook { s => + lint.add(if (s) w.name else s"-${w.name}") + } // withDeprecationMessage s"Enable -Xlint:${c._1}" + case _ => } private lazy val warnSelectNullable = BooleanSetting("-Xcheck-null", "This option is obsolete and does nothing.") diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index 0b8029326e..eda0c27834 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -158,6 +158,8 @@ class SettingsTest { assertTrue(check("-m:-d,ac")(_.value == Set(a,c))) assertTrue(check("-m:-b,ac,uber")(_.value == Set(a,c,d))) + assertFalse(check("-m:uber")(_.contains("i-m-not-an-option"))) + assertThrows[IllegalArgumentException](check("-m:-_")(_ => true), _ contains "'-_' is not a valid choice") assertThrows[IllegalArgumentException](check("-m:a,b,-ab")(_ => true), _ contains "'ab' cannot be negated") assertThrows[IllegalArgumentException](check("-m:a,ac,-uber,uber")(_ => true), _ contains "'uber' cannot be negated") -- cgit v1.2.3 From 1ea684341ba9eeff7c6bc6f943fb2d048a9ff5a3 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 8 Sep 2014 19:25:47 +0200 Subject: Make MultiChoiceSetting.compute easier to understand --- .../scala/tools/nsc/settings/MutableSettings.scala | 29 ++++++++-------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 1f75afd49a..bbe21477cb 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -604,10 +604,9 @@ class MutableSettings(val errorFn: String => Unit) withHelpSyntax(s"$name:<_,$helpArg,-$helpArg>") object ChoiceOrVal { - def unapply(a: Any): Option[(String, String, List[domain.Choice])] = a match { + def unapply(a: domain.Value): Option[(String, String, List[domain.Choice])] = a match { case c: domain.Choice => Some((c.name, c.help, c.expandsTo)) case v: domain.Value => Some((v.toString, "", Nil)) - case _ => None } } @@ -641,31 +640,25 @@ class MutableSettings(val errorFn: String => Unit) /** (Re)compute from current yeas, nays, wildcard status. */ def compute() = { - def nonExpanding(vs: domain.ValueSet) = vs filter { - case ChoiceOrVal(_, _, others) => others.isEmpty + def simple(v: domain.Value) = v match { + case ChoiceOrVal(_, _, Nil) => true + case _ => false } /** - * Expand an option, if necessary recursively. Expanding options are not included in the - * result (consistent with "_", which is not in the values either). Explicitly excluded - * options (in nays) are not added. + * Expand an expanding option, if necessary recursively. Expanding options are not included in + * the result (consistent with "_", which is not in `value` either). * * Note: by precondition, options in nays are not expanding, they can only be leaves. */ def expand(vs: domain.ValueSet): domain.ValueSet = vs flatMap { - // expand - case c @ ChoiceOrVal(_, _, others) if others.nonEmpty => expand(domain.ValueSet(others: _*)) - case c @ ChoiceOrVal(_, _, _) if !nays(c) => domain.ValueSet(c) - case _ => domain.ValueSet.empty + case c @ ChoiceOrVal(_, _, Nil) => domain.ValueSet(c) + case ChoiceOrVal(_, _, others) => expand(domain.ValueSet(others: _*)) } - val expandedYeas = expand(yeas) - - // we include everything, except those explicitly disabled. - val expandedAll = if (sawAll) nonExpanding(domain.values) &~ nays - else domain.ValueSet.empty - - value = nonExpanding(yeas) | expandedYeas | expandedAll + // yeas from _ or expansions are weak: an explicit nay will disable them + val weakYeas = if (sawAll) domain.values filter simple else expand(yeas filterNot simple) + value = (yeas filter simple) | (weakYeas &~ nays) } /** Add a named choice to the multichoice value. */ -- cgit v1.2.3