From 45e5c50e4bcc3fc687ce88b2a961a266255786f2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 1 Mar 2024 15:14:05 +0100 Subject: [PATCH 1/4] Add -W enabling warnings --- .../tools/dotc/config/ScalaSettings.scala | 38 +++++++++-- .../dotty/tools/dotc/config/Settings.scala | 8 ++- tests/warn/i18559.check | 56 ++++++++++++++++ tests/warn/i18559.scala | 66 +++++++++++++++++++ 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 tests/warn/i18559.check create mode 100644 tests/warn/i18559.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 687adfe05ca7..5175d5efa367 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -3,7 +3,7 @@ package config import scala.language.unsafeNulls import dotty.tools.dotc.config.PathResolver.Defaults -import dotty.tools.dotc.config.Settings.{Setting, SettingGroup, SettingCategory} +import dotty.tools.dotc.config.Settings.{Setting, SettingGroup, SettingCategory, SettingsState} import dotty.tools.dotc.config.SourceVersion import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.rewrites.Rewrites @@ -156,13 +156,42 @@ private sealed trait VerboseSettings: private sealed trait WarningSettings: self: SettingGroup => - val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "W", "Print a synopsis of warning options.") + val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "Whelp", "Print a synopsis of warning options.") + + val W: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + WarningSetting, + name = "W", + helpArg = "warning", + descr = "Enable sets of warnings or print a synopsis of warning options.", + choices = List( + ChoiceWithHelp("default", "Enable default warnings"), + ChoiceWithHelp("all", "Enable all warnings"), + ), + default = Nil + ) + + enum WarningGroup(val enabledBy: Set[String]): + case Default extends WarningGroup(Set("default", "all")) + case All extends WarningGroup(Set("all")) + + def overrideValueWithW[T](group: WarningGroup, overrideWhen: T, overrideTo: T)(ss: SettingsState, v: T): T = + if W.valueIn(ss).map(_.toString).exists(group.enabledBy.contains) && v == overrideWhen then overrideTo + else v + + def overrideWithW(group: WarningGroup)(ss: SettingsState, v: Boolean): Boolean = + overrideValueWithW(group, false, true)(ss, v) + val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) + .mapValue(overrideWithW(WarningGroup.Default)) val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "Wvalue-discard", "Warn when non-Unit expression results are unused.") + .mapValue(overrideWithW(WarningGroup.Default)) val WNonUnitStatement = BooleanSetting(WarningSetting, "Wnonunit-statement", "Warn when block statements are non-Unit expressions.") + .mapValue(overrideWithW(WarningGroup.Default)) val WenumCommentDiscard = BooleanSetting(WarningSetting, "Wenum-comment-discard", "Warn when a comment ambiguously assigned to multiple enum cases is discarded.") val WimplausiblePatterns = BooleanSetting(WarningSetting, "Wimplausible-patterns", "Warn if comparison with a pattern value looks like it might always fail.") + .mapValue(overrideWithW(WarningGroup.Default)) val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") + .mapValue(overrideWithW(WarningGroup.Default)) val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "Wunused", @@ -195,7 +224,8 @@ private sealed trait WarningSettings: ) ), default = Nil - ) + ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List(ChoiceWithHelp("linted", "")))) + object WunusedHas: def isChoiceSet(s: String)(using Context) = Wunused.value.pipe(us => us.contains(s)) def allOr(s: String)(using Context) = Wunused.value.pipe(us => us.contains("all") || us.contains(s)) @@ -282,7 +312,7 @@ private sealed trait WarningSettings: ChoiceWithHelp("type-parameter-shadow", "Warn when a type parameter shadows a type already in the scope"), ), default = Nil - ) + ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List(ChoiceWithHelp("all", "")))) object WshadowHas: def allOr(s: String)(using Context) = diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index a65072427ba7..869eec602ca0 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -82,7 +82,9 @@ object Settings: propertyClass: Option[Class[?]] = None, deprecationMsg: Option[String] = None, // kept only for -Ykind-projector option compatibility - legacyArgs: Boolean = false)(private[Settings] val idx: Int) { + legacyArgs: Boolean = false, + mapValue: (SettingsState, T) => T = (a, b: T) => b)(private[Settings] val idx: Int) { + validateSettingString(prefix.getOrElse(name)) aliases.foreach(validateSettingString) @@ -94,7 +96,7 @@ object Settings: val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases - def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T] + def valueIn(state: SettingsState): T = mapValue(state, state.value(idx).asInstanceOf[T]) def updateIn(state: SettingsState, x: Any): SettingsState = x match case _: T => state.update(idx, x) @@ -114,6 +116,8 @@ object Settings: case None => "" } + def mapValue(f: (SettingsState, T) => T): Setting[T] = copy(mapValue = f)(idx) + def tryToSet(state: ArgsSummary): ArgsSummary = { val ArgsSummary(sstate, arg :: args, errors, warnings) = state: @unchecked def update(value: Any, args: List[String]): ArgsSummary = diff --git a/tests/warn/i18559.check b/tests/warn/i18559.check new file mode 100644 index 000000000000..354385e78ebe --- /dev/null +++ b/tests/warn/i18559.check @@ -0,0 +1,56 @@ +-- [E176] Potential Issue Warning: tests/warn/i18559.scala:63:4 -------------------------------------------------------- +63 | improved // warn + | ^^^^^^^^ + | unused value of type (improved : => scala.concurrent.Future[Int]) +-- [E175] Potential Issue Warning: tests/warn/i18559.scala:51:35 ------------------------------------------------------- +51 | firstThing().map(_ => secondThing()) // warn + | ^^^^^^^^^^^^^ + | discarded non-Unit value of type Either[Failed, Unit] +-- [E175] Potential Issue Warning: tests/warn/i18559.scala:54:35 ------------------------------------------------------- +54 | firstThing().map(_ => secondThing()) // warn + | ^^^^^^^^^^^^^ + | discarded non-Unit value of type Either[Failed, Unit] +-- Warning: tests/warn/i18559.scala:4:6 -------------------------------------------------------------------------------- +4 | var e3 = 2 // warn + | ^^ + | unused local definition +-- Warning: tests/warn/i18559.scala:8:28 ------------------------------------------------------------------------------- +8 | import collection.mutable.Set // warn + | ^^^ + | unused import +-- Warning: tests/warn/i18559.scala:9:33 ------------------------------------------------------------------------------- +9 | import collection.mutable.{Map => MutMap} // warn + | ^^^^^^^^^^^^^ + | unused import +-- Warning: tests/warn/i18559.scala:10:28 ------------------------------------------------------------------------------ +10 | import collection.mutable._ // warn + | ^ + | unused import +-- Warning: tests/warn/i18559.scala:13:28 ------------------------------------------------------------------------------ +13 | import collection.mutable._ // warn + | ^ + | unused import +-- [E030] Match case Unreachable Warning: tests/warn/i18559.scala:31:10 ------------------------------------------------ +31 | case Sum(a@S(_),Z) => Z // warn + | ^^^^^^^^^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/warn/i18559.scala:32:10 ------------------------------------------------ +32 | case Sum(a@S(_),Z) => a // warn unreachable + | ^^^^^^^^^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/warn/i18559.scala:33:10 ------------------------------------------------ +33 | case Sum(a@S(b@S(_)), Z) => a // warn + | ^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/warn/i18559.scala:34:10 ------------------------------------------------ +34 | case Sum(a@S(b@S(_)), Z) => a // warn + | ^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/warn/i18559.scala:35:10 ------------------------------------------------ +35 | case Sum(a@S(b@(S(_))), Z) => Sum(a,b) // warn unreachable + | ^^^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E121] Pattern Match Warning: tests/warn/i18559.scala:37:7 ---------------------------------------------------------- +37 | case _ => Z // warn unreachable + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). diff --git a/tests/warn/i18559.scala b/tests/warn/i18559.scala new file mode 100644 index 000000000000..0eda23fafbc6 --- /dev/null +++ b/tests/warn/i18559.scala @@ -0,0 +1,66 @@ +//> using options -W:default + +val b = // OK + var e3 = 2 // warn + 1 + +object FooUnused: + import collection.mutable.Set // warn + import collection.mutable.{Map => MutMap} // warn + import collection.mutable._ // warn + +object FooWildcardUnused: + import collection.mutable._ // warn + +object Foo: + import collection.mutable.Set // OK + import collection.mutable.{Map => MutMap} // OK + + val bar = Set() // OK + val baz = MutMap() // OK + +sealed trait Calc +sealed trait Const extends Calc +case class Sum(a: Calc, b: Calc) extends Calc +case class S(pred: Const) extends Const +case object Z extends Const + +val a = Sum(S(S(Z)),Z) match { + case Sum(a,Z) => Z // not warn: patvars not enabled by Wall + // case Sum(a @ _,Z) => Z // todo : this should pass in the future + case Sum(a@S(_),Z) => Z // warn + case Sum(a@S(_),Z) => a // warn unreachable + case Sum(a@S(b@S(_)), Z) => a // warn + case Sum(a@S(b@S(_)), Z) => a // warn + case Sum(a@S(b@(S(_))), Z) => Sum(a,b) // warn unreachable + case Sum(_,_) => Z // OK + case _ => Z // warn unreachable +} +import scala.util.{Either, Right, Left} +import scala.collection.mutable + +case class Failed(msg: String) + +def firstThing(): Either[Failed, Unit] = + Right(()) + +def secondThing(): Either[Failed, Unit] = + Left(Failed("whoops you should have flatMapped me")) + +def singleExpr(): Either[Failed, Unit] = + firstThing().map(_ => secondThing()) // warn + +def block(): Either[Failed, Unit] = { + firstThing().map(_ => secondThing()) // warn +} + +class C { + import concurrent._ + import ExecutionContext.Implicits._ + def c = { + def improved = Future(42) + def stale = Future(27) + improved // warn + stale + } +} \ No newline at end of file From 10d41e6c8a432f9a708cb8fd0ba42d89ae80742e Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 19 Mar 2024 19:12:14 +0100 Subject: [PATCH 2/4] WIP on -W --- .../tools/dotc/config/CompilerCommand.scala | 9 ++++++--- .../dotty/tools/dotc/config/ScalaSettings.scala | 6 +++--- .../src/dotty/tools/dotc/config/Settings.scala | 16 ++++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 587f94dad7b3..3911bd4c1dc1 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -9,11 +9,14 @@ abstract class CompilerCommand extends CliCommand: final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match - case Some(s) => s.description + case Some(s) => + println("Hello!!") // To chyba nie CliCommand?? + s.description case _ => + println("No: " + settings.W.value) if (settings.help.value) usageMessage else if (settings.Vhelp.value) vusageMessage - else if (settings.Whelp.value) wusageMessage + else if (settings.W.value.exists(_.name == "help")) wusageMessage else if (settings.Xhelp.value) xusageMessage else if (settings.Yhelp.value) yusageMessage else if (settings.showPlugins.value) ctx.base.pluginDescriptions @@ -22,5 +25,5 @@ abstract class CompilerCommand extends CliCommand: final def isHelpFlag(using settings: ConcreteSettings)(using SettingsState): Boolean = import settings.* - val flags = Set(help, Vhelp, Whelp, Xhelp, Yhelp, showPlugins, XshowPhases) + val flags = Set(help, Vhelp, Xhelp, Yhelp, showPlugins, XshowPhases) flags.exists(_.value) || allSettings.exists(isHelping) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 5175d5efa367..4e7e3c6de6b6 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -156,18 +156,18 @@ private sealed trait VerboseSettings: private sealed trait WarningSettings: self: SettingGroup => - val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "Whelp", "Print a synopsis of warning options.") - val W: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "W", helpArg = "warning", descr = "Enable sets of warnings or print a synopsis of warning options.", choices = List( + ChoiceWithHelp("help", "Print a synopsis of warning options"), ChoiceWithHelp("default", "Enable default warnings"), ChoiceWithHelp("all", "Enable all warnings"), ), - default = Nil + default = Nil, + helpOnMissing = true ) enum WarningGroup(val enabledBy: Set[String]): diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 869eec602ca0..4bc7137a8466 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -83,16 +83,18 @@ object Settings: deprecationMsg: Option[String] = None, // kept only for -Ykind-projector option compatibility legacyArgs: Boolean = false, - mapValue: (SettingsState, T) => T = (a, b: T) => b)(private[Settings] val idx: Int) { + mapValue: (SettingsState, T) => T = (a, b: T) => b, + helpOnMissing: Boolean = false)(private[Settings] val idx: Int) { - + private val ct = summon[ClassTag[T]] validateSettingString(prefix.getOrElse(name)) aliases.foreach(validateSettingString) assert(name.startsWith(s"-${category.prefixLetter}"), s"Setting $name does not start with category -$category") assert(legacyArgs || !choices.exists(_.contains("")), s"Empty string is not supported as a choice for setting $name") // Without the following assertion, it would be easy to mistakenly try to pass a file to a setting that ignores invalid args. // Example: -opt Main.scala would be interpreted as -opt:Main.scala, and the source file would be ignored. - assert(!(summon[ClassTag[T]] == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name") + assert(!(ct == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name") + assert(!helpOnMissing || ct == StringTag || ct == ListTag, s"helpOnMissing is only supported for String or List settings: $name") val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases @@ -142,7 +144,9 @@ object Settings: def missingArg = val msg = s"missing argument for option $name" - if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) + if helpOnMissing then update("help", args) + else if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) + else fail(msg, args) def invalidChoices(invalid: List[String]) = val msg = s"invalid choice(s) for $name: ${invalid.mkString(",")}" @@ -349,8 +353,8 @@ object Settings: def MultiChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases)) - def MultiChoiceHelpSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = - publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases)) + def MultiChoiceHelpSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil, helpOnMissing: Boolean = false): Setting[List[ChoiceWithHelp[String]]] = + publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, helpOnMissing = helpOnMissing)) def IntSetting(category: SettingCategory, name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = publish(Setting(category, prependName(name), descr, default, aliases = aliases)) From 498208e0b48a169c81b293c329ed7958b02c8b0c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 20 Mar 2024 18:31:27 +0100 Subject: [PATCH 3/4] Working -W and refactored help on settings --- .../tools/dotc/config/CompilerCommand.scala | 23 +++-- .../tools/dotc/config/ScalaSettings.scala | 86 ++++++++++--------- .../dotty/tools/dotc/config/Settings.scala | 37 ++++---- .../dotc/config/ScalaSettingsTests.scala | 1 - 4 files changed, 71 insertions(+), 76 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 3911bd4c1dc1..efa23044dbd5 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -6,24 +6,23 @@ import core.Contexts.* abstract class CompilerCommand extends CliCommand: type ConcreteSettings = ScalaSettings + import ScalaSettings.* + private val customMessageFlags: Set[Setting[_]] = Set(ScalaSettings.W) final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = - settings.allSettings.find(isHelping) match - case Some(s) => - println("Hello!!") // To chyba nie CliCommand?? + allSettings.find(isHelping) match + case Some(s) if !customMessageFlags.contains(s) => s.description case _ => - println("No: " + settings.W.value) - if (settings.help.value) usageMessage - else if (settings.Vhelp.value) vusageMessage - else if (settings.W.value.exists(_.name == "help")) wusageMessage - else if (settings.Xhelp.value) xusageMessage - else if (settings.Yhelp.value) yusageMessage - else if (settings.showPlugins.value) ctx.base.pluginDescriptions - else if (settings.XshowPhases.value) phasesMessage + if (help.value) usageMessage + else if (Vhelp.value) vusageMessage + else if (W.value.contains("help")) wusageMessage + else if (Xhelp.value) xusageMessage + else if (Yhelp.value) yusageMessage + else if (showPlugins.value) ctx.base.pluginDescriptions + else if (XshowPhases.value) phasesMessage else "" final def isHelpFlag(using settings: ConcreteSettings)(using SettingsState): Boolean = - import settings.* val flags = Set(help, Vhelp, Xhelp, Yhelp, showPlugins, XshowPhases) flags.exists(_.value) || allSettings.exists(isHelping) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 4e7e3c6de6b6..db8ce6912dca 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -8,7 +8,6 @@ import dotty.tools.dotc.config.SourceVersion import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.rewrites.Rewrites import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory, NoAbstractFile} -import Setting.ChoiceWithHelp import ScalaSettingCategories.* import scala.util.chaining.* @@ -156,16 +155,17 @@ private sealed trait VerboseSettings: private sealed trait WarningSettings: self: SettingGroup => - val W: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + val W: Setting[List[String]] = MultiChoiceSetting( WarningSetting, name = "W", helpArg = "warning", descr = "Enable sets of warnings or print a synopsis of warning options.", - choices = List( - ChoiceWithHelp("help", "Print a synopsis of warning options"), - ChoiceWithHelp("default", "Enable default warnings"), - ChoiceWithHelp("all", "Enable all warnings"), - ), + choices = List("help", "default", "all"), + choiceHelp = Some(Map( + "help" -> "Print a synopsis of warning options", + "default" -> "Enable default warnings", + "all" -> "Enable all warnings" + )), default = Nil, helpOnMissing = true ) @@ -182,7 +182,6 @@ private sealed trait WarningSettings: overrideValueWithW(group, false, true)(ss, v) val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) - .mapValue(overrideWithW(WarningGroup.Default)) val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "Wvalue-discard", "Warn when non-Unit expression results are unused.") .mapValue(overrideWithW(WarningGroup.Default)) val WNonUnitStatement = BooleanSetting(WarningSetting, "Wnonunit-statement", "Warn when block statements are non-Unit expressions.") @@ -192,39 +191,43 @@ private sealed trait WarningSettings: .mapValue(overrideWithW(WarningGroup.Default)) val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") .mapValue(overrideWithW(WarningGroup.Default)) - val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + val Wunused: Setting[List[String]] = MultiChoiceSetting( WarningSetting, name = "Wunused", helpArg = "warning", descr = "Enable or disable specific `unused` warnings", choices = List( - ChoiceWithHelp("nowarn", ""), - ChoiceWithHelp("all",""), - ChoiceWithHelp( - name = "imports", - description = "Warn if an import selector is not referenced.\n" + + "nowarn", + "all", + "imports", + "privates", + "locals", + "explicits", + "implicits", + "params", + "linted", + "strict-no-implicit-warn", + "unsafe-warn-patvars" + ), + choiceHelp = Some(Map( + "nowarn" -> "", + "all" -> "", + "imports" -> ("Warn if an import selector is not referenced.\n" + "NOTE : overrided by -Wunused:strict-no-implicit-warn"), - ChoiceWithHelp("privates","Warn if a private member is unused"), - ChoiceWithHelp("locals","Warn if a local definition is unused"), - ChoiceWithHelp("explicits","Warn if an explicit parameter is unused"), - ChoiceWithHelp("implicits","Warn if an implicit parameter is unused"), - ChoiceWithHelp("params","Enable -Wunused:explicits,implicits"), - ChoiceWithHelp("linted","Enable -Wunused:imports,privates,locals,implicits"), - ChoiceWithHelp( - name = "strict-no-implicit-warn", - description = "Same as -Wunused:import, only for imports of explicit named members.\n" + - "NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all" - ), - // ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"), - ChoiceWithHelp( - name = "unsafe-warn-patvars", - description = "(UNSAFE) Warn if a variable bound in a pattern is unused.\n" + - "This warning can generate false positive, as warning cannot be\n" + - "suppressed yet." - ) - ), + "privates" -> "Warn if a private member is unused", + "locals" -> "Warn if a local definition is unused", + "explicits" -> "Warn if an explicit parameter is unused", + "implicits" -> "Warn if an implicit parameter is unused", + "params" -> "Enable -Wunused:explicits,implicits", + "linted" -> "Enable -Wunused:imports,privates,locals,implicits", + "strict-no-implicit-warn" -> ("Same as -Wunused:import, only for imports of explicit named members.\n" + + "NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all"), + "unsafe-warn-patvars" -> ("(UNSAFE) Warn if a variable bound in a pattern is unused.\n" + + "This warning can generate false positive, as warning cannot be\n" + + "suppressed yet.") + )), default = Nil - ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List(ChoiceWithHelp("linted", "")))) + ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List("linted"))) object WunusedHas: def isChoiceSet(s: String)(using Context) = Wunused.value.pipe(us => us.contains(s)) @@ -301,18 +304,19 @@ private sealed trait WarningSettings: |to prevent the shell from expanding patterns.""".stripMargin, ) - val Wshadow: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + val Wshadow: Setting[List[String]] = MultiChoiceSetting( WarningSetting, name = "Wshadow", helpArg = "warning", descr = "Enable or disable specific `shadow` warnings", - choices = List( - ChoiceWithHelp("all", ""), - ChoiceWithHelp("private-shadow", "Warn if a private field or class parameter shadows a superclass field"), - ChoiceWithHelp("type-parameter-shadow", "Warn when a type parameter shadows a type already in the scope"), - ), + choices = List("all", "private-shadow", "type-parameter-shadow"), + choiceHelp = Some(Map( + "all" -> "", + "private-shadow" -> "Warn if a private field or class parameter shadows a superclass field", + "type-parameter-shadow" -> "Warn when a type parameter shadows a type already in the scope" + )), default = Nil - ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List(ChoiceWithHelp("all", "")))) + ).mapValue(overrideValueWithW(WarningGroup.Default, Nil, List("all"))) object WshadowHas: def allOr(s: String)(using Context) = diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 4bc7137a8466..811293581410 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -13,7 +13,6 @@ import collection.mutable.ArrayBuffer import collection.mutable import reflect.ClassTag import scala.util.{Success, Failure} -import dotty.tools.dotc.config.Settings.Setting.ChoiceWithHelp object Settings: @@ -84,7 +83,8 @@ object Settings: // kept only for -Ykind-projector option compatibility legacyArgs: Boolean = false, mapValue: (SettingsState, T) => T = (a, b: T) => b, - helpOnMissing: Boolean = false)(private[Settings] val idx: Int) { + helpOnMissing: Boolean = false, + choiceHelp: Option[Map[Any, String]] = None)(private[Settings] val idx: Int) { private val ct = summon[ClassTag[T]] validateSettingString(prefix.getOrElse(name)) @@ -95,6 +95,7 @@ object Settings: // Example: -opt Main.scala would be interpreted as -opt:Main.scala, and the source file would be ignored. assert(!(ct == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name") assert(!helpOnMissing || ct == StringTag || ct == ListTag, s"helpOnMissing is only supported for String or List settings: $name") + assert(choices.isDefined || choiceHelp.isEmpty, s"choiceHelp is only supported for settings with choices: $name") val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases @@ -109,12 +110,17 @@ object Settings: def isMultivalue: Boolean = summon[ClassTag[T]] == ListTag def acceptsNoArg: Boolean = summon[ClassTag[T]] == BooleanTag || summon[ClassTag[T]] == OptionTag || choices.exists(_.contains("")) - + s"\n- $name${if description.isEmpty() then "" else s" :\n\t${description.replace("\n","\n\t")}"}" def legalChoices: String = + def toString[T](choice: T) = + choiceHelp match + case Some(choiceMap) => s"\n- $choice${if choiceMap.isDefinedAt(choice) then s":\n\t${choiceMap(choice).replace("\n","\n\t")}" else ""}" + case None => choice.toString + choices match { case Some(xs) if xs.isEmpty => "" case Some(r: Range) => s"${r.head}..${r.last}" - case Some(xs) => xs.mkString(", ") + case Some(xs) => xs.map(toString).mkString(", ") case None => "" } @@ -144,7 +150,9 @@ object Settings: def missingArg = val msg = s"missing argument for option $name" - if helpOnMissing then update("help", args) + if helpOnMissing then + val helpValue = if ct == StringTag then "help" else List("help") + update(helpValue, args) else if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) @@ -253,18 +261,6 @@ object Settings: def value(using Context): T = setting.valueIn(ctx.settingsState) def update(x: T)(using Context): SettingsState = setting.updateIn(ctx.settingsState, x) def isDefault(using Context): Boolean = setting.isDefaultIn(ctx.settingsState) - - /** - * A choice with help description. - * - * NOTE : `equals` and `toString` have special behaviors - */ - case class ChoiceWithHelp[T](name: T, description: String): - override def equals(x: Any): Boolean = x match - case s:String => s == name.toString() - case _ => false - override def toString(): String = - s"\n- $name${if description.isEmpty() then "" else s" :\n\t${description.replace("\n","\n\t")}"}" end Setting class SettingGroup { @@ -350,11 +346,8 @@ object Settings: def ChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil, legacyArgs: Boolean = false): Setting[String] = publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, legacyArgs = legacyArgs)) - def MultiChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases)) - - def MultiChoiceHelpSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil, helpOnMissing: Boolean = false): Setting[List[ChoiceWithHelp[String]]] = - publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, helpOnMissing = helpOnMissing)) + def MultiChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil, helpOnMissing: Boolean = false, choiceHelp: Option[Map[Any, String]] = None): Setting[List[String]] = + publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, helpOnMissing = helpOnMissing, choiceHelp = choiceHelp)) def IntSetting(category: SettingCategory, name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = publish(Setting(category, prependName(name), descr, default, aliases = aliases)) diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index e958a5925fce..28aa573ca417 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -96,5 +96,4 @@ class ScalaSettingsTests: assertEquals(Action.Silent, sut.action(depr)) - end ScalaSettingsTests From 55d91c4d697eee3ca10c3c9c07a9b6e1a38a6de9 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 22 Mar 2024 15:32:37 +0100 Subject: [PATCH 4/4] Add tests --- .../tools/dotc/config/ScalaSettingsTests.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 28aa573ca417..0ae5d1fff505 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -95,5 +95,20 @@ class ScalaSettingsTests: val sut = reporting.WConf.fromSettings(conf).getOrElse(???) assertEquals(Action.Silent, sut.action(depr)) + @Test def `Treat parameterless -W as help`: Unit = + val sets = config.ScalaSettings + val args = List("-W") + val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) + val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) + assertTrue(sets.W.valueIn(sumy.sstate).contains("help")) + + @Test def `Enable default warns with -W default`: Unit = + val sets = config.ScalaSettings + val args = List("-W", "default") + val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) + val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) + assertTrue(sets.Wunused.valueIn(sumy.sstate).contains("linted")) + assertTrue(sets.WNonUnitStatement.valueIn(sumy.sstate)) + assertTrue(sets.WvalueDiscard.valueIn(sumy.sstate)) end ScalaSettingsTests