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