From 23d0ce3ab8461fe7faae38194385dadf390a6d17 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 22 Feb 2024 17:18:58 +0100 Subject: [PATCH 01/15] Refactor settings & improve dx --- .../tools/backend/jvm/BackendUtils.scala | 39 +- compiler/src/dotty/tools/dotc/Driver.scala | 2 + .../tools/dotc/config/CompilerCommand.scala | 6 +- .../tools/dotc/config/ScalaSettings.scala | 441 +++++++++--------- .../dotty/tools/dotc/config/Settings.scala | 200 ++++---- .../src/dotty/tools/dotc/core/Contexts.scala | 5 +- .../dotty/tools/dotc/ScalaCommandTest.scala | 4 +- .../test/dotty/tools/dotc/SettingsTests.scala | 30 +- .../dotc/config/ScalaSettingsTests.scala | 12 +- 9 files changed, 393 insertions(+), 346 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index 2f8a469169cc..459f1b2b2613 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -20,24 +20,7 @@ class BackendUtils(val postProcessor: PostProcessor) { import bTypes.* import coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle - // Keep synchronized with `minTargetVersion` and `maxTargetVersion` in ScalaSettings - lazy val classfileVersion: Int = compilerSettings.target match { - case "8" => asm.Opcodes.V1_8 - case "9" => asm.Opcodes.V9 - case "10" => asm.Opcodes.V10 - case "11" => asm.Opcodes.V11 - case "12" => asm.Opcodes.V12 - case "13" => asm.Opcodes.V13 - case "14" => asm.Opcodes.V14 - case "15" => asm.Opcodes.V15 - case "16" => asm.Opcodes.V16 - case "17" => asm.Opcodes.V17 - case "18" => asm.Opcodes.V18 - case "19" => asm.Opcodes.V19 - case "20" => asm.Opcodes.V20 - case "21" => asm.Opcodes.V21 - case "22" => asm.Opcodes.V22 - } + lazy val classfileVersion: Int = BackendUtils.classfileVersionMap(compilerSettings.target) lazy val extraProc: Int = { import GenBCodeOps.addFlagIf @@ -184,3 +167,23 @@ class BackendUtils(val postProcessor: PostProcessor) { } } } + +object BackendUtils { + lazy val classfileVersionMap = Map( + "8" -> asm.Opcodes.V1_8, + "9" -> asm.Opcodes.V9, + "10" -> asm.Opcodes.V10, + "11" -> asm.Opcodes.V11, + "12" -> asm.Opcodes.V12, + "13" -> asm.Opcodes.V13, + "14" -> asm.Opcodes.V14, + "15" -> asm.Opcodes.V15, + "16" -> asm.Opcodes.V16, + "17" -> asm.Opcodes.V17, + "18" -> asm.Opcodes.V18, + "19" -> asm.Opcodes.V19, + "20" -> asm.Opcodes.V20, + "21" -> asm.Opcodes.V21, + "22" -> asm.Opcodes.V22, + ) +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 196752aceb29..82a9f5850bb6 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -10,6 +10,7 @@ import dotty.tools.io.AbstractFile import reporting.* import core.Decorators.* import config.Feature +import dotty.tools.dotc.config.ScalaSettings import scala.util.control.NonFatal import fromtasty.{TASTYCompiler, TastyFileUtil} @@ -199,6 +200,7 @@ class Driver { } def main(args: Array[String]): Unit = { + println("settings: " + ScalaSettings.allSettings) // Preload scala.util.control.NonFatal. Otherwise, when trying to catch a StackOverflowError, // we may try to load it but fail with another StackOverflowError and lose the original exception, // see . diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 2ffe900fbdbf..2e82c6a3cd67 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -5,9 +5,9 @@ import Settings.* import core.Contexts.* abstract class CompilerCommand extends CliCommand: - type ConcreteSettings = ScalaSettings + type ConcreteSettings = ScalaSettings.type - final def helpMsg(using settings: ScalaSettings)(using SettingsState, Context): String = + final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match case Some(s) => s.description case _ => @@ -20,7 +20,7 @@ abstract class CompilerCommand extends CliCommand: else if (settings.XshowPhases.value) phasesMessage else "" - final def isHelpFlag(using settings: ScalaSettings)(using SettingsState): Boolean = + final def isHelpFlag(using settings: ConcreteSettings)(using SettingsState): Boolean = import settings.* val flags = Set(help, Vhelp, Whelp, 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 e970e148f615..92150ae6e426 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package config import scala.language.unsafeNulls - +import dotty.tools.backend.jvm.BackendUtils.classfileVersionMap import dotty.tools.dotc.config.PathResolver.Defaults import dotty.tools.dotc.config.Settings.{Setting, SettingGroup} import dotty.tools.dotc.config.SourceVersion @@ -10,17 +10,35 @@ 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.* import java.util.zip.Deflater -class ScalaSettings extends SettingGroup with AllScalaSettings - -object ScalaSettings: - // Keep synchronized with `classfileVersion` in `BackendUtils` - private val minTargetVersion = 8 - private val maxTargetVersion = 22 +object ScalaSettingCategories: + val RootSetting = "" + val WarningSetting = "W" + val ForkSetting = "Y" + val AdvancedSetting = "X" + val VerboseSetting = "V" + +object ScalaSettings extends SettingGroup with AllScalaSettings: + + val settingsByCategory: Map[String, List[Setting[_]]] = + allSettings.groupBy(_.category) + .view.mapValues(_.toList).toMap + .withDefaultValue(Nil) + def categories: List[String] = settingsByCategory.keys.toList + val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) + val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) + val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) + val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) + val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) + val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap + + private lazy val minTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).min + private lazy val maxTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).max def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) @@ -56,38 +74,38 @@ trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSetti self: SettingGroup => /* Path related settings */ - val semanticdbTarget: Setting[String] = PathSetting("-semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "") - val semanticdbText: Setting[Boolean] = BooleanSetting("-semanticdb-text", "Specifies whether to include source code in SemanticDB files or not.") + val semanticdbTarget: Setting[String] = PathSetting(RootSetting, "semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "") + val semanticdbText: Setting[Boolean] = BooleanSetting(RootSetting, "semanticdb-text", "Specifies whether to include source code in SemanticDB files or not.") - val source: Setting[String] = ChoiceSetting("-source", "source version", "source version", ScalaSettings.supportedSourceVersions, SourceVersion.defaultSourceVersion.toString, aliases = List("--source")) - val uniqid: Setting[Boolean] = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.", aliases = List("--unique-id")) - val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.", aliases = List("--rewrite")) - val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty files. The arguments are .tasty or .jar files.", aliases = List("--from-tasty")) + val source: Setting[String] = ChoiceSetting(RootSetting, "source", "source version", "source version", ScalaSettings.supportedSourceVersions, SourceVersion.defaultSourceVersion.toString, aliases = List("--source")) + val uniqid: Setting[Boolean] = BooleanSetting(RootSetting, "uniqid", "Uniquely tag all identifiers in debugging output.", aliases = List("--unique-id")) + val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites](RootSetting, "rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.", aliases = List("--rewrite")) + val fromTasty: Setting[Boolean] = BooleanSetting(RootSetting, "from-tasty", "Compile classes from tasty files. The arguments are .tasty or .jar files.", aliases = List("--from-tasty")) - val newSyntax: Setting[Boolean] = BooleanSetting("-new-syntax", "Require `then` and `do` in control expressions.") - val oldSyntax: Setting[Boolean] = BooleanSetting("-old-syntax", "Require `(...)` around conditions.") - val indent: Setting[Boolean] = BooleanSetting("-indent", "Together with -rewrite, remove {...} syntax when possible due to significant indentation.") - val noindent: Setting[Boolean] = BooleanSetting("-no-indent", "Require classical {...} syntax, indentation is not significant.", aliases = List("-noindent")) + val newSyntax: Setting[Boolean] = BooleanSetting(RootSetting, "new-syntax", "Require `then` and `do` in control expressions.") + val oldSyntax: Setting[Boolean] = BooleanSetting(RootSetting, "old-syntax", "Require `(...)` around conditions.") + val indent: Setting[Boolean] = BooleanSetting(RootSetting, "indent", "Together with -rewrite, remove {...} syntax when possible due to significant indentation.") + val noindent: Setting[Boolean] = BooleanSetting(RootSetting, "no-indent", "Require classical {...} syntax, indentation is not significant.", aliases = List("-noindent")) /* Decompiler settings */ - val printTasty: Setting[Boolean] = BooleanSetting("-print-tasty", "Prints the raw tasty.", aliases = List("--print-tasty")) - val printLines: Setting[Boolean] = BooleanSetting("-print-lines", "Show source code line numbers.", aliases = List("--print-lines")) + val printTasty: Setting[Boolean] = BooleanSetting(RootSetting, "print-tasty", "Prints the raw tasty.", aliases = List("--print-tasty")) + val printLines: Setting[Boolean] = BooleanSetting(RootSetting, "print-lines", "Show source code line numbers.", aliases = List("--print-lines")) /* Scala.js-related settings */ - val scalajsGenStaticForwardersForNonTopLevelObjects: Setting[Boolean] = BooleanSetting("-scalajs-genStaticForwardersForNonTopLevelObjects", "Generate static forwarders even for non-top-level objects (Scala.js only).") - val scalajsMapSourceURI: Setting[List[String]] = MultiStringSetting("-scalajs-mapSourceURI", "uri1[->uri2]", "rebases source URIs from uri1 to uri2 (or to a relative URI) for source maps (Scala.js only).") + val scalajsGenStaticForwardersForNonTopLevelObjects: Setting[Boolean] = BooleanSetting(RootSetting, "scalajs-genStaticForwardersForNonTopLevelObjects", "Generate static forwarders even for non-top-level objects (Scala.js only).") + val scalajsMapSourceURI: Setting[List[String]] = MultiStringSetting(RootSetting, "scalajs-mapSourceURI", "uri1[->uri2]", "rebases source URIs from uri1 to uri2 (or to a relative URI) for source maps (Scala.js only).") val projectUrl: Setting[String] = StringSetting ( - "-project-url", + RootSetting, + "project-url", "project repository homepage", "The source repository of your project.", "" ) - val wikiSyntax: Setting[Boolean] = BooleanSetting("-Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc.") - - val jvmargs = PrefixSetting("-J", "-J", "Pass directly to the runtime system.") - val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") + val wikiSyntax: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc.") + val jvmargs = PrefixSetting(RootSetting, "J", "Pass -J directly to the runtime system.") + val defines = PrefixSetting(RootSetting, "D", "Pass -D directly to the runtime system.") end AllScalaSettings /** Settings shared by compiler and scaladoc */ @@ -95,84 +113,85 @@ trait CommonScalaSettings: self: SettingGroup => /* Path related settings */ - val bootclasspath: Setting[String] = PathSetting("-bootclasspath", "Override location of bootstrap class files.", Defaults.scalaBootClassPath, aliases = List("--boot-class-path")) - val extdirs: Setting[String] = PathSetting("-extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs, aliases = List("--extension-directories")) - val javabootclasspath: Setting[String] = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath, aliases = List("--java-boot-class-path")) - val javaextdirs: Setting[String] = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs, aliases = List("--java-extension-directories")) - val sourcepath: Setting[String] = PathSetting("-sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath, aliases = List("--source-path")) - val sourceroot: Setting[String] = PathSetting("-sourceroot", "Specify workspace root directory.", ".") - - val classpath: Setting[String] = PathSetting("-classpath", "Specify where to find user class files.", ScalaSettings.defaultClasspath, aliases = List("-cp", "--class-path")) - val outputDir: Setting[AbstractFile] = OutputSetting("-d", "directory|jar", "Destination for generated classfiles.", + val bootclasspath: Setting[String] = PathSetting(RootSetting, "bootclasspath", "Override location of bootstrap class files.", Defaults.scalaBootClassPath, aliases = List("--boot-class-path")) + val extdirs: Setting[String] = PathSetting(RootSetting, "extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs, aliases = List("--extension-directories")) + val javabootclasspath: Setting[String] = PathSetting(RootSetting, "javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath, aliases = List("--java-boot-class-path")) + val javaextdirs: Setting[String] = PathSetting(RootSetting, "javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs, aliases = List("--java-extension-directories")) + val sourcepath: Setting[String] = PathSetting(RootSetting, "sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath, aliases = List("--source-path")) + val sourceroot: Setting[String] = PathSetting(RootSetting, "sourceroot", "Specify workspace root directory.", ".") + + val classpath: Setting[String] = PathSetting(RootSetting, "classpath", "Specify where to find user class files.", ScalaSettings.defaultClasspath, aliases = List("-cp", "--class-path")) + val outputDir: Setting[AbstractFile] = OutputSetting(RootSetting, "d", "directory|jar", "Destination for generated classfiles.", new PlainDirectory(Directory("."))) - val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/, aliases = List("--color")) - val verbose: Setting[Boolean] = BooleanSetting("-verbose", "Output messages about what the compiler is doing.", aliases = List("--verbose")) - val version: Setting[Boolean] = BooleanSetting("-version", "Print product version and exit.", aliases = List("--version")) - val help: Setting[Boolean] = BooleanSetting("-help", "Print a synopsis of standard options.", aliases = List("--help", "-h")) - val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", ScalaSettings.defaultPageWidth, aliases = List("--page-width")) - val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.", aliases = List("--no-warnings")) - - val javaOutputVersion: Setting[String] = ChoiceSetting("-java-output-version", "version", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version. Corresponds to -release flag in javac.", ScalaSettings.supportedReleaseVersions, "", aliases = List("-release", "--release")) - - val deprecation: Setting[Boolean] = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.", aliases = List("--deprecation")) - val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature")) - val explain: Setting[Boolean] = BooleanSetting("-explain", "Explain errors in more detail.", aliases = List("--explain")) + val color: Setting[String] = ChoiceSetting(RootSetting, "color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/, aliases = List("--color")) + val verbose: Setting[Boolean] = BooleanSetting(RootSetting, "verbose", "Output messages about what the compiler is doing.", aliases = List("--verbose")) + val version: Setting[Boolean] = BooleanSetting(RootSetting, "version", "Print product version and exit.", aliases = List("--version")) + val help: Setting[Boolean] = BooleanSetting(RootSetting, "help", "Print a synopsis of standard options.", aliases = List("--help", "-h")) + val pageWidth: Setting[Int] = IntSetting(RootSetting, "pagewidth", "Set page width", ScalaSettings.defaultPageWidth, aliases = List("--page-width")) + val silentWarnings: Setting[Boolean] = BooleanSetting(RootSetting, "nowarn", "Silence all warnings.", aliases = List("--no-warnings")) + + val javaOutputVersion: Setting[String] = ChoiceSetting(RootSetting, "java-output-version", "version", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version. Corresponds to -release flag in javac.", ScalaSettings.supportedReleaseVersions, "", aliases = List("-release", "--release")) + + val deprecation: Setting[Boolean] = BooleanSetting(RootSetting, "deprecation", "Emit warning and location for usages of deprecated APIs.", aliases = List("--deprecation")) + val feature: Setting[Boolean] = BooleanSetting(RootSetting, "feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature")) + val explain: Setting[Boolean] = BooleanSetting(RootSetting, "explain", "Explain errors in more detail.", aliases = List("--explain")) // -explain-types setting is necessary for cross compilation, since it is mentioned in sbt-tpolecat, for instance // it is otherwise subsumed by -explain, and should be dropped as soon as we can. - val explainTypes: Setting[Boolean] = BooleanSetting("-explain-types", "Explain type errors in more detail (deprecated, use -explain instead).", aliases = List("--explain-types", "-explaintypes")) - val explainCyclic: Setting[Boolean] = BooleanSetting("-explain-cyclic", "Explain cyclic reference errors in more detail.", aliases = List("--explain-cyclic")) - val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked")) - val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.", aliases = List("--language")) - val experimental: Setting[Boolean] = BooleanSetting("-experimental", "Annotate all top-level definitions with @experimental. This enables the use of experimental features anywhere in the project.") + val explainTypes: Setting[Boolean] = BooleanSetting(RootSetting, "explain-types", "Explain type errors in more detail (deprecated, use -explain instead).", aliases = List("--explain-types", "-explaintypes")) + val explainCyclic: Setting[Boolean] = BooleanSetting(RootSetting, "explain-cyclic", "Explain cyclic reference errors in more detail.", aliases = List("--explain-cyclic")) + val unchecked: Setting[Boolean] = BooleanSetting(RootSetting, "unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked")) + val language: Setting[List[String]] = MultiStringSetting(RootSetting, "language", "feature", "Enable one or more language features.", aliases = List("--language")) + val experimental: Setting[Boolean] = BooleanSetting(RootSetting, "experimental", "Annotate all top-level definitions with @experimental. This enables the use of experimental features anywhere in the project.") /* Coverage settings */ - val coverageOutputDir = PathSetting("-coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out")) - val coverageExcludeClasslikes: Setting[List[String]] = MultiStringSetting("-coverage-exclude-classlikes", "packages, classes and modules", "List of regexes for packages, classes and modules to exclude from coverage.", aliases = List("--coverage-exclude-classlikes")) - val coverageExcludeFiles: Setting[List[String]] = MultiStringSetting("-coverage-exclude-files", "files", "List of regexes for files to exclude from coverage.", aliases = List("--coverage-exclude-files")) + val coverageOutputDir = PathSetting(RootSetting, "coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out")) + val coverageExcludeClasslikes: Setting[List[String]] = MultiStringSetting(RootSetting, "coverage-exclude-classlikes", "packages, classes and modules", "List of regexes for packages, classes and modules to exclude from coverage.", aliases = List("--coverage-exclude-classlikes")) + val coverageExcludeFiles: Setting[List[String]] = MultiStringSetting(RootSetting, "coverage-exclude-files", "files", "List of regexes for files to exclude from coverage.", aliases = List("--coverage-exclude-files")) /* Other settings */ - val encoding: Setting[String] = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding, aliases = List("--encoding")) - val usejavacp: Setting[Boolean] = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.", aliases = List("--use-java-class-path")) - val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).", aliases = List("--scalajs")) + val encoding: Setting[String] = StringSetting(RootSetting, "encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding, aliases = List("--encoding")) + val usejavacp: Setting[Boolean] = BooleanSetting(RootSetting, "usejavacp", "Utilize the java.class.path in classpath resolution.", aliases = List("--use-java-class-path")) + val scalajs: Setting[Boolean] = BooleanSetting(RootSetting, "scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).", aliases = List("--scalajs")) end CommonScalaSettings /** -P "plugin" settings. Various tools might support plugins. */ private sealed trait PluginSettings: self: SettingGroup => - val plugin: Setting[List[String]] = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") - val disable: Setting[List[String]] = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") - val require: Setting[List[String]] = MultiStringSetting ("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") - val showPlugins: Setting[Boolean] = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") - val pluginsDir: Setting[String] = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) - val pluginOptions: Setting[List[String]] = MultiStringSetting ("-P", "plugin:opt", "Pass an option to a plugin, e.g. -P::") + val plugin: Setting[List[String]] = MultiStringSetting (AdvancedSetting, "Xplugin", "paths", "Load a plugin from each classpath.") + val disable: Setting[List[String]] = MultiStringSetting (AdvancedSetting, "Xplugin-disable", "plugin", "Disable plugins by name.") + val require: Setting[List[String]] = MultiStringSetting (AdvancedSetting, "Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") + val showPlugins: Setting[Boolean] = BooleanSetting (AdvancedSetting, "Xplugin-list", "Print a synopsis of loaded plugins.") + val pluginsDir: Setting[String] = StringSetting (AdvancedSetting, "Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) + val pluginOptions: Setting[List[String]] = MultiStringSetting (RootSetting, "P", "plugin:opt", "Pass an option to a plugin, e.g. -P::") /** -V "Verbose" settings */ private sealed trait VerboseSettings: self: SettingGroup => - val Vhelp: Setting[Boolean] = BooleanSetting("-V", "Print a synopsis of verbose options.") - val Xprint: Setting[List[String]] = PhasesSetting("-Vprint", "Print out program after", aliases = List("-Xprint")) - val XshowPhases: Setting[Boolean] = BooleanSetting("-Vphases", "List compiler phases.", aliases = List("-Xshow-phases")) + val Vhelp: Setting[Boolean] = BooleanSetting(VerboseSetting, "V", "Print a synopsis of verbose options.") + val Xprint: Setting[List[String]] = PhasesSetting(VerboseSetting, "Vprint", "Print out program after", aliases = List("-Xprint")) + val XshowPhases: Setting[Boolean] = BooleanSetting(VerboseSetting, "Vphases", "List compiler phases.", aliases = List("-Xshow-phases")) - val Vprofile: Setting[Boolean] = BooleanSetting("-Vprofile", "Show metrics about sources and internal representations to estimate compile-time complexity.") - val VprofileSortedBy = ChoiceSetting("-Vprofile-sorted-by", "key", "Show metrics about sources and internal representations sorted by given column name", List("name", "path", "lines", "tokens", "tasty", "complexity"), "") - val VprofileDetails = IntSetting("-Vprofile-details", "Show metrics about sources and internal representations of the most complex methods", 0) - val VreplMaxPrintElements: Setting[Int] = IntSetting("-Vrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000) - val VreplMaxPrintCharacters: Setting[Int] = IntSetting("-Vrepl-max-print-characters", "Number of characters to be printed before output is truncated.", 50000) + val Vprofile: Setting[Boolean] = BooleanSetting(VerboseSetting, "Vprofile", "Show metrics about sources and internal representations to estimate compile-time complexity.") + val VprofileSortedBy = ChoiceSetting(VerboseSetting, "Vprofile-sorted-by", "key", "Show metrics about sources and internal representations sorted by given column name", List("name", "path", "lines", "tokens", "tasty", "complexity"), "") + val VprofileDetails = IntSetting(VerboseSetting, "Vprofile-details", "Show metrics about sources and internal representations of the most complex methods", 0) + val VreplMaxPrintElements: Setting[Int] = IntSetting(VerboseSetting, "Vrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000) + val VreplMaxPrintCharacters: Setting[Int] = IntSetting(VerboseSetting, "Vrepl-max-print-characters", "Number of characters to be printed before output is truncated.", 50000) /** -W "Warnings" settings */ private sealed trait WarningSettings: self: SettingGroup => - val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.") - val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) - val WvalueDiscard: Setting[Boolean] = BooleanSetting("-Wvalue-discard", "Warn when non-Unit expression results are unused.") - val WNonUnitStatement = BooleanSetting("-Wnonunit-statement", "Warn when block statements are non-Unit expressions.") - val WenumCommentDiscard = BooleanSetting("-Wenum-comment-discard", "Warn when a comment ambiguously assigned to multiple enum cases is discarded.") - val WimplausiblePatterns = BooleanSetting("-Wimplausible-patterns", "Warn if comparison with a pattern value looks like it might always fail.") - val WunstableInlineAccessors = BooleanSetting("-WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") + val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "-W", "Print a synopsis of warning options.") + val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) + val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "-Wvalue-discard", "Warn when non-Unit expression results are unused.") + val WNonUnitStatement = BooleanSetting(WarningSetting, "-Wnonunit-statement", "Warn when block statements are non-Unit expressions.") + 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.") + val WunstableInlineAccessors = BooleanSetting(WarningSetting, "-WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( - name = "-Wunused", + WarningSetting, + name = "Wunused", helpArg = "warning", descr = "Enable or disable specific `unused` warnings", choices = List( @@ -231,7 +250,8 @@ private sealed trait WarningSettings: isChoiceSet("strict-no-implicit-warn") val Wconf: Setting[List[String]] = MultiStringSetting( - "-Wconf", + WarningSetting, + "Wconf", "patterns", default = List(), descr = @@ -277,47 +297,9 @@ private sealed trait WarningSettings: |to prevent the shell from expanding patterns.""".stripMargin, ) -/** -X "Extended" or "Advanced" settings */ -private sealed trait XSettings: - self: SettingGroup => - - val Xhelp: Setting[Boolean] = BooleanSetting("-X", "Print a synopsis of advanced options.") - val XnoForwarders: Setting[Boolean] = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") - val XmaxInlines: Setting[Int] = IntSetting("-Xmax-inlines", "Maximal number of successive inlines.", 32) - val XmaxInlinedTrees: Setting[Int] = IntSetting("-Xmax-inlined-trees", "Maximal number of inlined trees.", 2_000_000) - val Xmigration: Setting[ScalaVersion] = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.") - val XprintTypes: Setting[Boolean] = BooleanSetting("-Xprint-types", "Print tree types (debugging option).") - val XprintDiff: Setting[Boolean] = BooleanSetting("-Xprint-diff", "Print changed parts of the tree since last print.") - val XprintDiffDel: Setting[Boolean] = BooleanSetting("-Xprint-diff-del", "Print changed parts of the tree since last print including deleted parts.") - val XprintInline: Setting[Boolean] = BooleanSetting("-Xprint-inline", "Show where inlined code comes from.") - val XprintSuspension: Setting[Boolean] = BooleanSetting("-Xprint-suspension", "Show when code is suspended until macros are compiled.") - val Xprompt: Setting[Boolean] = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).") - val XreplDisableDisplay: Setting[Boolean] = BooleanSetting("-Xrepl-disable-display", "Do not display definitions in REPL.") - val XverifySignatures: Setting[Boolean] = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.") - val XignoreScala2Macros: Setting[Boolean] = BooleanSetting("-Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.") - val XimportSuggestionTimeout: Setting[Int] = IntSetting("-Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) - val Xsemanticdb: Setting[Boolean] = BooleanSetting("-Xsemanticdb", "Store information in SemanticDB.", aliases = List("-Ysemanticdb")) - val XuncheckedJavaOutputVersion: Setting[String] = ChoiceSetting("-Xunchecked-java-output-version", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. Corresponds to -target flag in javac. When on JDK 9+, consider -java-output-version as a safer alternative.", ScalaSettings.supportedTargetVersions, "", aliases = List("-Xtarget", "--Xtarget")) - val XcheckMacros: Setting[Boolean] = BooleanSetting("-Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros")) - val XmainClass: Setting[String] = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d )", "") - val XimplicitSearchLimit: Setting[Int] = IntSetting("-Ximplicit-search-limit", "Maximal number of expressions to be generated in an implicit search", 50000) - - val XmixinForceForwarders = ChoiceSetting( - name = "-Xmixin-force-forwarders", - helpArg = "mode", - descr = "Generate forwarder methods in classes inhering concrete methods from traits.", - choices = List("true", "junit", "false"), - default = "true") - - object mixinForwarderChoices { - def isTruthy(using Context) = XmixinForceForwarders.value == "true" - def isAtLeastJunit(using Context) = isTruthy || XmixinForceForwarders.value == "junit" - } - - val XmacroSettings: Setting[List[String]] = MultiStringSetting("-Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") - val Xlint: Setting[List[ChoiceWithHelp[String]]] = UncompleteMultiChoiceHelpSetting( - name = "-Xlint", + AdvancedSetting, + name = "Xlint", helpArg = "advanced warning", descr = "Enable or disable specific `lint` warnings", choices = List( @@ -336,111 +318,150 @@ private sealed trait XSettings: def typeParameterShadow(using Context) = allOr("type-parameter-shadow") + +/** -X "Extended" or "Advanced" settings */ +private sealed trait XSettings: + self: SettingGroup => + + val Xhelp: Setting[Boolean] = BooleanSetting(AdvancedSetting, "X", "Print a synopsis of advanced options.") + val XnoForwarders: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xno-forwarders", "Do not generate static forwarders in mirror classes.") + val XmaxInlines: Setting[Int] = IntSetting(AdvancedSetting, "Xmax-inlines", "Maximal number of successive inlines.", 32) + val XmaxInlinedTrees: Setting[Int] = IntSetting(AdvancedSetting, "Xmax-inlined-trees", "Maximal number of inlined trees.", 2_000_000) + val Xmigration: Setting[ScalaVersion] = VersionSetting(AdvancedSetting, "Xmigration", "Warn about constructs whose behavior may have changed since version.") + val XprintTypes: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-types", "Print tree types (debugging option).") + val XprintDiff: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-diff", "Print changed parts of the tree since last print.") + val XprintDiffDel: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-diff-del", "Print changed parts of the tree since last print including deleted parts.") + val XprintInline: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-inline", "Show where inlined code comes from.") + val XprintSuspension: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-suspension", "Show when code is suspended until macros are compiled.") + val Xprompt: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprompt", "Display a prompt after each error (debugging option).") + val XreplDisableDisplay: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xrepl-disable-display", "Do not display definitions in REPL.") + val XverifySignatures: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xverify-signatures", "Verify generic signatures in generated bytecode.") + val XignoreScala2Macros: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.") + val XimportSuggestionTimeout: Setting[Int] = IntSetting(AdvancedSetting, "Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) + val Xsemanticdb: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xsemanticdb", "Store information in SemanticDB.", aliases = List("-Ysemanticdb")) + val XuncheckedJavaOutputVersion: Setting[String] = ChoiceSetting(AdvancedSetting, "Xunchecked-java-output-version", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. Corresponds to -target flag in javac. When on JDK 9+, consider -java-output-version as a safer alternative.", ScalaSettings.supportedTargetVersions, "", aliases = List("-Xtarget", "--Xtarget")) + val XcheckMacros: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros")) + val XmainClass: Setting[String] = StringSetting(AdvancedSetting, "Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d )", "") + val XimplicitSearchLimit: Setting[Int] = IntSetting(AdvancedSetting, "Ximplicit-search-limit", "Maximal number of expressions to be generated in an implicit search", 50000) + + val XmixinForceForwarders = ChoiceSetting( + AdvancedSetting, + name = "Xmixin-force-forwarders", + helpArg = "mode", + descr = "Generate forwarder methods in classes inhering concrete methods from traits.", + choices = List("true", "junit", "false"), + default = "true") + + object mixinForwarderChoices { + def isTruthy(using Context) = XmixinForceForwarders.value == "true" + def isAtLeastJunit(using Context) = isTruthy || XmixinForceForwarders.value == "junit" + } + + val XmacroSettings: Setting[List[String]] = MultiStringSetting(AdvancedSetting, "Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") end XSettings /** -Y "Forking" as in forked tongue or "Private" settings */ private sealed trait YSettings: self: SettingGroup => - val Yhelp: Setting[Boolean] = BooleanSetting("-Y", "Print a synopsis of private options.") - val Ycheck: Setting[List[String]] = PhasesSetting("-Ycheck", "Check the tree at the end of") - val YcheckMods: Setting[Boolean] = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync.") - val Ydebug: Setting[Boolean] = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") - val YdebugTrace: Setting[Boolean] = BooleanSetting("-Ydebug-trace", "Trace core operations.") - val YdebugFlags: Setting[Boolean] = BooleanSetting("-Ydebug-flags", "Print all flags of definitions.") - val YdebugMissingRefs: Setting[Boolean] = BooleanSetting("-Ydebug-missing-refs", "Print a stacktrace when a required symbol is missing.") - val YdebugNames: Setting[Boolean] = BooleanSetting("-Ydebug-names", "Show internal representation of names.") - val YdebugPos: Setting[Boolean] = BooleanSetting("-Ydebug-pos", "Show full source positions including spans.") - val YdebugTreeWithId: Setting[Int] = IntSetting("-Ydebug-tree-with-id", "Print the stack trace when the tree with the given id is created.", Int.MinValue) - val YdebugTypeError: Setting[Boolean] = BooleanSetting("-Ydebug-type-error", "Print the stack trace when a TypeError is caught", false) - val YdebugError: Setting[Boolean] = BooleanSetting("-Ydebug-error", "Print the stack trace when any error is caught.", false) - val YdebugUnpickling: Setting[Boolean] = BooleanSetting("-Ydebug-unpickling", "Print the stack trace when an error occurs when reading Tasty.", false) - val YdebugCyclic: Setting[Boolean] = BooleanSetting("-Ydebug-cyclic", "Print the stack trace when a cyclic reference error occurs.", false) - val YtermConflict: Setting[String] = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") - val Ylog: Setting[List[String]] = PhasesSetting("-Ylog", "Log operations during") - val YlogClasspath: Setting[Boolean] = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") - val YdisableFlatCpCaching: Setting[Boolean] = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") - - val Yscala2Unpickler: Setting[String] = StringSetting("-Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always") - - val YnoImports: Setting[Boolean] = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") - val Yimports: Setting[List[String]] = MultiStringSetting("-Yimports", helpArg="", "Custom root imports. If set, none of scala.*, java.lang.*, or Predef.* will be imported unless explicitly included.") - val YnoGenericSig: Setting[Boolean] = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") - val YnoPredef: Setting[Boolean] = BooleanSetting("-Yno-predef", "Compile without importing Predef.") - val Yskip: Setting[List[String]] = PhasesSetting("-Yskip", "Skip") - val Ydumpclasses: Setting[String] = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") - val YjarCompressionLevel: Setting[Int] = IntChoiceSetting("-Yjar-compression-level", "compression level to use when writing jar files", Deflater.DEFAULT_COMPRESSION to Deflater.BEST_COMPRESSION, Deflater.DEFAULT_COMPRESSION) - val YbackendParallelism: Setting[Int] = IntChoiceSetting("-Ybackend-parallelism", "maximum worker threads for backend", 1 to 16, 1) - val YbackendWorkerQueue: Setting[Int] = IntChoiceSetting("-Ybackend-worker-queue", "backend threads worker queue size", 0 to 1000, 0) - val YstopAfter: Setting[List[String]] = PhasesSetting("-Ystop-after", "Stop after", aliases = List("-stop")) // backward compat - val YstopBefore: Setting[List[String]] = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully - val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") - val YdetailedStats: Setting[Boolean] = BooleanSetting("-Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") - val YkindProjector: Setting[String] = ChoiceSetting("-Ykind-projector", "[underscores, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") - val YprintPos: Setting[Boolean] = BooleanSetting("-Yprint-pos", "Show tree positions.") - val YprintPosSyms: Setting[Boolean] = BooleanSetting("-Yprint-pos-syms", "Show symbol definitions positions.") - val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting("-Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") - val YnoPatmatOpt: Setting[Boolean] = BooleanSetting("-Yno-patmat-opt", "Disable all pattern matching optimizations.") - val YplainPrinter: Setting[Boolean] = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.") - val YprintSyms: Setting[Boolean] = BooleanSetting("-Yprint-syms", "When printing trees print info in symbols instead of corresponding info in trees.") - val YprintDebug: Setting[Boolean] = BooleanSetting("-Yprint-debug", "When printing trees, print some extra information useful for debugging.") - val YprintDebugOwners: Setting[Boolean] = BooleanSetting("-Yprint-debug-owners", "When printing trees, print owners of definitions.") - val YprintLevel: Setting[Boolean] = BooleanSetting("-Yprint-level", "print nesting levels of symbols and type variables.") - val YshowPrintErrors: Setting[Boolean] = BooleanSetting("-Yshow-print-errors", "Don't suppress exceptions thrown during tree printing.") - val YprintTasty: Setting[Boolean] = BooleanSetting("-Yprint-tasty", "Prints the generated TASTY to stdout.") - val YtestPickler: Setting[Boolean] = BooleanSetting("-Ytest-pickler", "Self-test for pickling functionality; should be used with -Ystop-after:pickler.") - val YtestPicklerCheck: Setting[Boolean] = BooleanSetting("-Ytest-pickler-check", "Self-test for pickling -print-tasty output; should be used with -Ytest-pickler.") - val YcheckReentrant: Setting[Boolean] = BooleanSetting("-Ycheck-reentrant", "Check that compiled program does not contain vars that can be accessed from a global root.") - val YdropComments: Setting[Boolean] = BooleanSetting("-Ydrop-docs", "Drop documentation when scanning source files.", aliases = List("-Ydrop-comments")) - val YcookComments: Setting[Boolean] = BooleanSetting("-Ycook-docs", "Cook the documentation (type check `@usecase`, etc.)", aliases = List("-Ycook-comments")) - val YreadComments: Setting[Boolean] = BooleanSetting("-Yread-docs", "Read documentation from tasty.") - val YforceSbtPhases: Setting[Boolean] = BooleanSetting("-Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.") - val YdumpSbtInc: Setting[Boolean] = BooleanSetting("-Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.") - val YcheckAllPatmat: Setting[Boolean] = BooleanSetting("-Ycheck-all-patmat", "Check exhaustivity and redundancy of all pattern matching (used for testing the algorithm).") - val YcheckConstraintDeps: Setting[Boolean] = BooleanSetting("-Ycheck-constraint-deps", "Check dependency tracking in constraints (used for testing the algorithm).") - val YretainTrees: Setting[Boolean] = BooleanSetting("-Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree") - val YshowTreeIds: Setting[Boolean] = BooleanSetting("-Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.") - val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting("-Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty.") - val YnoExperimental: Setting[Boolean] = BooleanSetting("-Yno-experimental", "Disable experimental language features by default in NIGHTLY/SNAPSHOT versions of the compiler.") - val YlegacyLazyVals: Setting[Boolean] = BooleanSetting("-Ylegacy-lazy-vals", "Use legacy (pre 3.3.0) implementation of lazy vals.") - val YcompileScala2Library: Setting[Boolean] = BooleanSetting("-Ycompile-scala2-library", "Used when compiling the Scala 2 standard library.") - val YoutputOnlyTasty: Setting[Boolean] = BooleanSetting("-Youtput-only-tasty", "Used to only generate the TASTy file without the classfiles") - - val YprofileEnabled: Setting[Boolean] = BooleanSetting("-Yprofile-enabled", "Enable profiling.") - val YprofileDestination: Setting[String] = StringSetting("-Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "") + val Yhelp: Setting[Boolean] = BooleanSetting(ForkSetting, "Y", "Print a synopsis of private options.") + val Ycheck: Setting[List[String]] = PhasesSetting(ForkSetting, "Ycheck", "Check the tree at the end of") + val YcheckMods: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync.") + val Ydebug: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug", "Increase the quantity of debugging output.") + val YdebugTrace: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-trace", "Trace core operations.") + val YdebugFlags: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-flags", "Print all flags of definitions.") + val YdebugMissingRefs: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-missing-refs", "Print a stacktrace when a required symbol is missing.") + val YdebugNames: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-names", "Show internal representation of names.") + val YdebugPos: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-pos", "Show full source positions including spans.") + val YdebugTreeWithId: Setting[Int] = IntSetting(ForkSetting, "Ydebug-tree-with-id", "Print the stack trace when the tree with the given id is created.", Int.MinValue) + val YdebugTypeError: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-type-error", "Print the stack trace when a TypeError is caught", false) + val YdebugError: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-error", "Print the stack trace when any error is caught.", false) + val YdebugUnpickling: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-unpickling", "Print the stack trace when an error occurs when reading Tasty.", false) + val YdebugCyclic: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-cyclic", "Print the stack trace when a cyclic reference error occurs.", false) + val YtermConflict: Setting[String] = ChoiceSetting(ForkSetting, "Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") + val Ylog: Setting[List[String]] = PhasesSetting(ForkSetting, "Ylog", "Log operations during") + val YlogClasspath: Setting[Boolean] = BooleanSetting(ForkSetting, "Ylog-classpath", "Output information about what classpath is being applied.") + val YdisableFlatCpCaching: Setting[Boolean] = BooleanSetting(ForkSetting, "YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") + + val Yscala2Unpickler: Setting[String] = StringSetting(ForkSetting, "Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always") + + val YnoImports: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") + val Yimports: Setting[List[String]] = MultiStringSetting(ForkSetting, "Yimports", helpArg="", "Custom root imports. If set, none of scala.*, java.lang.*, or Predef.* will be imported unless explicitly included.") + val YnoGenericSig: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-generic-signatures", "Suppress generation of generic signatures for Java.") + val YnoPredef: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-predef", "Compile without importing Predef.") + val Yskip: Setting[List[String]] = PhasesSetting(ForkSetting, "Yskip", "Skip") + val Ydumpclasses: Setting[String] = StringSetting(ForkSetting, "Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") + val YjarCompressionLevel: Setting[Int] = IntChoiceSetting(ForkSetting, "Yjar-compression-level", "compression level to use when writing jar files", Deflater.DEFAULT_COMPRESSION to Deflater.BEST_COMPRESSION, Deflater.DEFAULT_COMPRESSION) + val YbackendParallelism: Setting[Int] = IntChoiceSetting(ForkSetting, "Ybackend-parallelism", "maximum worker threads for backend", 1 to 16, 1) + val YbackendWorkerQueue: Setting[Int] = IntChoiceSetting(ForkSetting, "Ybackend-worker-queue", "backend threads worker queue size", 0 to 1000, 0) + val YstopAfter: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-after", "Stop after", aliases = List("-stop")) // backward compat + val YstopBefore: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully + val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") + val YdetailedStats: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") + val YkindProjector: Setting[String] = ChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") + val YprintPos: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos", "Show tree positions.") + val YprintPosSyms: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos-syms", "Show symbol definitions positions.") + val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") + val YnoPatmatOpt: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-patmat-opt", "Disable all pattern matching optimizations.") + val YplainPrinter: Setting[Boolean] = BooleanSetting(ForkSetting, "Yplain-printer", "Pretty-print using a plain printer.") + val YprintSyms: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-syms", "When printing trees print info in symbols instead of corresponding info in trees.") + val YprintDebug: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-debug", "When printing trees, print some extra information useful for debugging.") + val YprintDebugOwners: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-debug-owners", "When printing trees, print owners of definitions.") + val YprintLevel: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-level", "print nesting levels of symbols and type variables.") + val YshowPrintErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-print-errors", "Don't suppress exceptions thrown during tree printing.") + val YprintTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-tasty", "Prints the generated TASTY to stdout.") + val YtestPickler: Setting[Boolean] = BooleanSetting(ForkSetting, "Ytest-pickler", "Self-test for pickling functionality; should be used with -Ystop-after:pickler.") + val YtestPicklerCheck: Setting[Boolean] = BooleanSetting(ForkSetting, "Ytest-pickler-check", "Self-test for pickling -print-tasty output; should be used with -Ytest-pickler.") + val YcheckReentrant: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycheck-reentrant", "Check that compiled program does not contain vars that can be accessed from a global root.") + val YdropComments: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydrop-docs", "Drop documentation when scanning source files.", aliases = List("-Ydrop-comments")) + val YcookComments: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycook-docs", "Cook the documentation (type check `@usecase`, etc.)", aliases = List("-Ycook-comments")) + val YreadComments: Setting[Boolean] = BooleanSetting(ForkSetting, "Yread-docs", "Read documentation from tasty.") + val YforceSbtPhases: Setting[Boolean] = BooleanSetting(ForkSetting, "Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.") + val YdumpSbtInc: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.") + val YcheckAllPatmat: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycheck-all-patmat", "Check exhaustivity and redundancy of all pattern matching (used for testing the algorithm).") + val YcheckConstraintDeps: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycheck-constraint-deps", "Check dependency tracking in constraints (used for testing the algorithm).") + val YretainTrees: Setting[Boolean] = BooleanSetting(ForkSetting, "Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree") + val YshowTreeIds: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.") + val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting(ForkSetting, "Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty.") + val YnoExperimental: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-experimental", "Disable experimental language features by default in NIGHTLY/SNAPSHOT versions of the compiler.") + val YlegacyLazyVals: Setting[Boolean] = BooleanSetting(ForkSetting, "Ylegacy-lazy-vals", "Use legacy (pre 3.3.0) implementation of lazy vals.") + val YcompileScala2Library: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycompile-scala2-library", "Used when compiling the Scala 2 standard library.") + val YoutputOnlyTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Youtput-only-tasty", "Used to only generate the TASTy file without the classfiles") + val YprofileEnabled: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprofile-enabled", "Enable profiling.") + val YprofileDestination: Setting[String] = StringSetting(ForkSetting, "Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "") //.withPostSetHook( _ => YprofileEnabled.value = true ) - val YprofileExternalTool: Setting[List[String]] = PhasesSetting("-Yprofile-external-tool", "Enable profiling for a phase using an external tool hook. Generally only useful for a single phase.", "typer") + val YprofileExternalTool: Setting[List[String]] = PhasesSetting(ForkSetting, "Yprofile-external-tool", "Enable profiling for a phase using an external tool hook. Generally only useful for a single phase.", "typer") //.withPostSetHook( _ => YprofileEnabled.value = true ) - val YprofileRunGcBetweenPhases: Setting[List[String]] = PhasesSetting("-Yprofile-run-gc", "Run a GC between phases - this allows heap size to be accurate at the expense of more time. Specify a list of phases, or *", "_") + val YprofileRunGcBetweenPhases: Setting[List[String]] = PhasesSetting(ForkSetting, "Yprofile-run-gc", "Run a GC between phases - this allows heap size to be accurate at the expense of more time. Specify a list of phases, or *", "_") //.withPostSetHook( _ => YprofileEnabled.value = true ) // Experimental language features - val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Disable kind polymorphism.") - val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.") - val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects.") - val YcheckInitGlobal: Setting[Boolean] = BooleanSetting("-Ysafe-init-global", "Check safe initialization of global objects.") - val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation.") - val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only).") - val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references.") - val YccNew: Setting[Boolean] = BooleanSetting("-Ycc-new", "Used in conjunction with captureChecking language import, try out new variants (debug option)") - val YccLog: Setting[Boolean] = BooleanSetting("-Ycc-log", "Used in conjunction with captureChecking language import, print tracing and debug info") - val YccPrintSetup: Setting[Boolean] = BooleanSetting("-Ycc-print-setup", "Used in conjunction with captureChecking language import, print trees after cc.Setup phase") + val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-kind-polymorphism", "Disable kind polymorphism.") + val YexplicitNulls: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.") + val YcheckInit: Setting[Boolean] = BooleanSetting(ForkSetting, "Ysafe-init", "Ensure safe initialization of objects.") + val YcheckInitGlobal: Setting[Boolean] = BooleanSetting(ForkSetting, "Ysafe-init-global", "Check safe initialization of global objects.") + val YrequireTargetName: Setting[Boolean] = BooleanSetting(ForkSetting, "Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation.") + val YrecheckTest: Setting[Boolean] = BooleanSetting(ForkSetting, "Yrecheck-test", "Run basic rechecking (internal test only).") + val YccDebug: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references.") + val YccNew: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycc-new", "Used in conjunction with captureChecking language import, try out new variants (debug option)") + val YccLog: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycc-log", "Used in conjunction with captureChecking language import, print tracing and debug info") + val YccPrintSetup: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycc-print-setup", "Used in conjunction with captureChecking language import, print trees after cc.Setup phase") /** Area-specific debug output */ - val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.") - val YnoDoubleBindings: Setting[Boolean] = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") - val YshowVarBounds: Setting[Boolean] = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds.") + val YexplainLowlevel: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplain-lowlevel", "When explaining type errors, show types at a lower level.") + val YnoDoubleBindings: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") + val YshowVarBounds: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-var-bounds", "Print type variables with their bounds.") - val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting("-Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.") - val YnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting("-Yno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.") + val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.") + val YnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.") - val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") - val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") + val Yinstrument: Setting[Boolean] = BooleanSetting(ForkSetting, "Yinstrument", "Add instrumentation code that counts allocations and closure creations.") + val YinstrumentDefs: Setting[Boolean] = BooleanSetting(ForkSetting, "Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") - val YdebugMacros: Setting[Boolean] = BooleanSetting("-Ydebug-macros", "Show debug info when quote pattern match fails") + val YdebugMacros: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydebug-macros", "Show debug info when quote pattern match fails") // Pipeline compilation options - val YjavaTasty: Setting[Boolean] = BooleanSetting("-Yjava-tasty", "Pickler phase should compute pickles for .java defined symbols for use by build tools") - val YjavaTastyOutput: Setting[AbstractFile] = OutputSetting("-Yjava-tasty-output", "directory|jar", "(Internal use only!) destination for generated .tasty files containing Java type signatures.", NoAbstractFile) - val YallowOutlineFromTasty: Setting[Boolean] = BooleanSetting("-Yallow-outline-from-tasty", "Allow outline TASTy to be loaded with the -from-tasty option.") + val YjavaTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Yjava-tasty", "Pickler phase should compute pickles for .java defined symbols for use by build tools") + val YjavaTastyOutput: Setting[AbstractFile] = OutputSetting(ForkSetting, "Yjava-tasty-output", "directory|jar", "(Internal use only!) destination for generated .tasty files containing Java type signatures.", NoAbstractFile) + val YallowOutlineFromTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Yallow-outline-from-tasty", "Allow outline TASTy to be loaded with the -from-tasty option.") end YSettings diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 79eb2b882f8f..961bca4da121 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -9,6 +9,7 @@ import dotty.tools.io.{AbstractFile, Directory, JarArchive, PlainDirectory} import annotation.tailrec import collection.mutable.ArrayBuffer +import collection.mutable import reflect.ClassTag import scala.util.{Success, Failure} import dotty.tools.dotc.config.Settings.Setting.ChoiceWithHelp @@ -23,8 +24,9 @@ object Settings: val OptionTag: ClassTag[Option[?]] = ClassTag(classOf[Option[?]]) val OutputTag: ClassTag[AbstractFile] = ClassTag(classOf[AbstractFile]) - class SettingsState(initialValues: Seq[Any]): + class SettingsState(initialValues: Seq[Any], initialChanged: Set[Int] = Set.empty): private val values = ArrayBuffer(initialValues*) + private val changed: mutable.Set[Int] = initialChanged.to(mutable.Set) private var _wasRead: Boolean = false override def toString: String = s"SettingsState(values: ${values.toList})" @@ -33,10 +35,13 @@ object Settings: _wasRead = true values(idx) + def wasChanged(idx: Int): Boolean = changed.contains(idx) + def update(idx: Int, x: Any): SettingsState = - if (_wasRead) then SettingsState(values.toSeq).update(idx, x) + if (_wasRead) then SettingsState(values.toSeq, changed.toSet).update(idx, x) else values(idx) = x + changed.add(idx) this end SettingsState @@ -54,18 +59,21 @@ object Settings: } case class Setting[T: ClassTag] private[Settings] ( + category: String, name: String, description: String, default: T, helpArg: String = "", choices: Option[Seq[?]] = None, - prefix: String = "", + prefix: Option[String] = None, aliases: List[String] = Nil, depends: List[(Setting[?], Any)] = Nil, ignoreInvalidArgs: Boolean = false, propertyClass: Option[Class[?]] = None)(private[Settings] val idx: Int) { - private var changed: Boolean = false + assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") + + val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T] @@ -77,6 +85,8 @@ object Settings: def isMultivalue: Boolean = summon[ClassTag[T]] == ListTag + def acceptsNoArg: Boolean = summon[ClassTag[T]] == BooleanTag || summon[ClassTag[T]] == OptionTag || choices.exists(_.contains("")) + def legalChoices: String = choices match { case Some(xs) if xs.isEmpty => "" @@ -89,17 +99,16 @@ object Settings: val ArgsSummary(sstate, arg :: args, errors, warnings) = state: @unchecked def update(value: Any, args: List[String]): ArgsSummary = var dangers = warnings - val value1 = - if changed && isMultivalue then - val value0 = value.asInstanceOf[List[String]] + val valueNew = + if sstate.wasChanged(idx) && isMultivalue then + val valueList = value.asInstanceOf[List[String]] val current = valueIn(sstate).asInstanceOf[List[String]] - value0.filter(current.contains).foreach(s => dangers :+= s"Setting $name set to $s redundantly") - current ++ value0 + valueList.filter(current.contains).foreach(s => dangers :+= s"Setting $name set to $s redundantly") + current ++ valueList else - if changed then dangers :+= s"Flag $name set repeatedly" + if sstate.wasChanged(idx) then dangers :+= s"Flag $name set repeatedly" value - changed = true - ArgsSummary(updateIn(sstate, value1), args, errors, dangers) + ArgsSummary(updateIn(sstate, valueNew), args, errors, dangers) end update def fail(msg: String, args: List[String]) = @@ -141,53 +150,56 @@ object Settings: catch case _: NumberFormatException => fail(s"$argValue is not an integer argument for $name", args) - def doSet(argRest: String) = ((summon[ClassTag[T]], args): @unchecked) match { - case (BooleanTag, _) => - setBoolean(argRest, args) - case (OptionTag, _) => - update(Some(propertyClass.get.getConstructor().newInstance()), args) - case (ListTag, _) => - if (argRest.isEmpty) missingArg - else - val strings = argRest.split(",").toList - choices match - case Some(valid) => strings.filterNot(valid.contains) match - case Nil => update(strings, args) - case invalid => invalidChoices(invalid) - case _ => update(strings, args) - case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => - setString(argRest, args) - case (StringTag, arg2 :: args2) => - if (arg2 startsWith "-") missingArg - else setString(arg2, args2) - case (OutputTag, arg :: args) => - val path = Directory(arg) - val isJar = path.extension == "jar" - if (!isJar && !path.isDirectory) - fail(s"'$arg' does not exist or is not a directory or .jar file", args) - else { - val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) - update(output, args) - } - case (IntTag, args) if argRest.nonEmpty => - setInt(argRest, args) - case (IntTag, arg2 :: args2) => - setInt(arg2, args2) - case (VersionTag, _) => - ScalaVersion.parse(argRest) match { - case Success(v) => update(v, args) - case Failure(ex) => fail(ex.getMessage, args) - } - case (_, Nil) => - missingArg - } - - def matches(argName: String) = (name :: aliases).exists(_ == argName) - - if (prefix != "" && arg.startsWith(prefix)) - doSet(arg drop prefix.length) - else if (prefix == "" && matches(arg.takeWhile(_ != ':'))) - doSet(arg.dropWhile(_ != ':').drop(1)) + def doSet(argRest: String) = + ((summon[ClassTag[T]], args): @unchecked) match { + case (BooleanTag, _) => + setBoolean(argRest, args) + case (OptionTag, _) => + update(Some(propertyClass.get.getConstructor().newInstance()), args) + case (ListTag, _) => + if (argRest.isEmpty) missingArg + else + val strings = argRest.split(",").toList + choices match + case Some(valid) => strings.filterNot(valid.contains) match + case Nil => update(strings, args) + case invalid => invalidChoices(invalid) + case _ => update(strings, args) + case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => + setString(argRest, args) + case (StringTag, arg2 :: args2) => + if (arg2 startsWith "-") missingArg + else setString(arg2, args2) + case (OutputTag, arg :: args) => + val path = Directory(arg) + val isJar = path.extension == "jar" + if (!isJar && !path.isDirectory) + fail(s"'$arg' does not exist or is not a directory or .jar file", args) + else { + val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) + update(output, args) + } + case (IntTag, args) if argRest.nonEmpty => + setInt(argRest, args) + case (IntTag, arg2 :: args2) => + setInt(arg2, args2) + case (VersionTag, _) => + ScalaVersion.parse(argRest) match { + case Success(v) => update(v, args) + case Failure(ex) => fail(ex.getMessage, args) + } + case (_, Nil) => + missingArg + } + + def matches(argName: String): Boolean = + (allFullNames).exists(_ == argName.takeWhile(_ != ':')) || prefix.exists(arg.startsWith) + + def argValRest: String = + if(prefix.isEmpty) arg.dropWhile(_ != ':').drop(1) else arg.drop(prefix.get.length) + + if matches(arg) then + doSet(argValRest) else state } @@ -281,49 +293,59 @@ object Settings: setting } - def BooleanSetting(name: String, descr: String, initialValue: Boolean = false, aliases: List[String] = Nil): Setting[Boolean] = - publish(Setting(name, descr, initialValue, aliases = aliases)) + val settingCharacters = "[a-zA-Z0-9_\\-]*".r + def validateSetting(setting: String): String = + assert(settingCharacters.matches(setting), s"Setting $setting contains invalid characters") + setting + + def validateAndPrependName(name: String): String = + assert(!name.startsWith("-"), s"Setting $name cannot start with -") + "-" + validateSetting(name) + + def BooleanSetting(category: String, name: String, descr: String, initialValue: Boolean = false, aliases: List[String] = Nil): Setting[Boolean] = + publish(Setting(category, validateAndPrependName(name), descr, initialValue, aliases = aliases.map(validateSetting))) - def StringSetting(name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(name, descr, default, helpArg, aliases = aliases)) + def StringSetting(category: String, name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, aliases = aliases.map(validateSetting))) - def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases)) + def ChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) - def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases)) + def MultiChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) - def MultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = - publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases)) + def MultiChoiceHelpSetting(category: String, 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, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) - def UncompleteMultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = - publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases, ignoreInvalidArgs = true)) + def UncompleteMultiChoiceHelpSetting(category: String, 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, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting), ignoreInvalidArgs = true)) - def IntSetting(name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = - publish(Setting(name, descr, default, aliases = aliases)) + def IntSetting(category: String, name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = + publish(Setting(category, validateAndPrependName(name), descr, default, aliases = aliases.map(validateSetting))) - def IntChoiceSetting(name: String, descr: String, choices: Seq[Int], default: Int): Setting[Int] = - publish(Setting(name, descr, default, choices = Some(choices))) + def IntChoiceSetting(category: String, name: String, descr: String, choices: Seq[Int], default: Int): Setting[Int] = + publish(Setting(category, validateAndPrependName(name), descr, default, choices = Some(choices))) - def MultiStringSetting(name: String, helpArg: String, descr: String, default: List[String] = Nil, aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(name, descr, default, helpArg, aliases = aliases)) + def MultiStringSetting(category: String, name: String, helpArg: String, descr: String, default: List[String] = Nil, aliases: List[String] = Nil): Setting[List[String]] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, aliases = aliases.map(validateSetting))) - def OutputSetting(name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] = - publish(Setting(name, descr, default, helpArg)) + def OutputSetting(category: String, name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg)) - def PathSetting(name: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(name, descr, default, aliases = aliases)) + def PathSetting(category: String, name: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, validateAndPrependName(name), descr, default, aliases = aliases.map(validateSetting))) - def PhasesSetting(name: String, descr: String, default: String = "", aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(name, descr, if (default.isEmpty) Nil else List(default), aliases = aliases)) + def PhasesSetting(category: String, name: String, descr: String, default: String = "", aliases: List[String] = Nil): Setting[List[String]] = + publish(Setting(category, validateAndPrependName(name), descr, if (default.isEmpty) Nil else List(default), aliases = aliases.map(validateSetting))) - def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] = - publish(Setting(name, descr, Nil, prefix = pre)) + def PrefixSetting(category: String, name: String, descr: String): Setting[List[String]] = + val prefix = name.takeWhile(_ != '<') + publish(Setting(category, "-" + name, descr, Nil, prefix = Some(validateSetting(prefix)))) - def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = - publish(Setting(name, descr, default)) + def VersionSetting(category: String, name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = + publish(Setting(category, validateAndPrependName(name), descr, default)) - def OptionSetting[T: ClassTag](name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = - publish(Setting(name, descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases)) + def OptionSetting[T: ClassTag](category: String, name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = + publish(Setting(category, validateAndPrependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases.map(validateSetting))) } end Settings diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index c5f04d18b7fb..b94824ac3a2b 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -513,7 +513,7 @@ object Contexts { s"""Context( |${outersIterator.map(ctx => cinfo(using ctx)).mkString("\n\n")})""".stripMargin - def settings: ScalaSettings = base.settings + def settings: ScalaSettings.type = base.settings def definitions: Definitions = base.definitions def platform: Platform = base.platform def pendingUnderlying: util.HashSet[Type] = base.pendingUnderlying @@ -861,8 +861,7 @@ object Contexts { with Phases.PhasesBase with Plugins { - /** The applicable settings */ - val settings: ScalaSettings = new ScalaSettings + val settings: ScalaSettings.type = ScalaSettings /** The initial context */ val initialCtx: Context = FreshContext.initial(this: @unchecked, settings) diff --git a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala index 53f063a9e767..852a232e4c6a 100644 --- a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala +++ b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala @@ -17,7 +17,7 @@ class ScalaCommandTest: def temporaryFolder = _temporaryFolder @Test def `Simple one parameter`: Unit = inContext { - val settings = config.ScalaSettings() + val settings = config.ScalaSettings val args = "-cp path/to/classes1:other/path/to/classes2 files".split(" ") val summary = ScalacCommand.distill(args, settings)() given SettingsState = summary.sstate @@ -26,7 +26,7 @@ class ScalaCommandTest: } @Test def `Unfold @file`: Unit = inContext { - val settings = config.ScalaSettings() + val settings = config.ScalaSettings val file = temporaryFolder.newFile("config") val writer = java.io.FileWriter(file); writer.write("-sourceroot myNewRoot someMoreFiles"); diff --git a/compiler/test/dotty/tools/dotc/SettingsTests.scala b/compiler/test/dotty/tools/dotc/SettingsTests.scala index 8c571a321548..416b3fcc8b91 100644 --- a/compiler/test/dotty/tools/dotc/SettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/SettingsTests.scala @@ -43,8 +43,8 @@ class SettingsTests { @Test def acceptUnconstrained: Unit = object Settings extends SettingGroup: - val foo = StringSetting("-foo", "foo", "Foo", "a") - val bar = IntSetting("-bar", "Bar", 0) + val foo = StringSetting("", "foo", "foo", "Foo", "a") + val bar = IntSetting("", "bar", "Bar", 0) val args = List("-foo", "b", "-bar", "1") val summary = Settings.processArguments(args, true) @@ -72,7 +72,7 @@ class SettingsTests { @Test def `dont crash on many options`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("-option", "Some option") + val option = BooleanSetting("", "-option", "Some option") val limit = 6000 val args = List.fill(limit)("-option") @@ -87,7 +87,7 @@ class SettingsTests { @Test def `bad option warning consumes an arg`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("-option", "Some option") + val option = BooleanSetting("", "-option", "Some option") val args = List("-adoption", "dogs", "cats") val summary = Settings.processArguments(args, processAll = true) @@ -97,7 +97,7 @@ class SettingsTests { @Test def `bad option settings throws`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("-option", "Some option") + val option = BooleanSetting("", "-option", "Some option") def checkMessage(s: String): (Throwable => Boolean) = t => if t.getMessage == s then true @@ -112,12 +112,12 @@ class SettingsTests { @Test def validateChoices: Unit = object Settings extends SettingGroup: - val foo = ChoiceSetting("-foo", "foo", "Foo", List("a", "b"), "a") - val bar = IntChoiceSetting("-bar", "Bar", List(0, 1, 2), 0) - val baz = IntChoiceSetting("-baz", "Baz", 0 to 10, 10) + val foo = ChoiceSetting("", "-foo", "foo", "Foo", List("a", "b"), "a") + val bar = IntChoiceSetting("", "-bar", "Bar", List(0, 1, 2), 0) + val baz = IntChoiceSetting("", "-baz", "Baz", 0 to 10, 10) - val quux = ChoiceSetting("-quux", "quux", "Quux", List(), "") - val quuz = IntChoiceSetting("-quuz", "Quuz", List(), 0) + val quux = ChoiceSetting("", "-quux", "quux", "Quux", List(), "") + val quuz = IntChoiceSetting("", "-quuz", "Quuz", List(), 0) locally { val args = List("-foo", "b", "-bar", "1", "-baz", "5") @@ -169,7 +169,7 @@ class SettingsTests { @Test def `Allow IntSetting's to be set with a colon`: Unit = object Settings extends SettingGroup: - val foo = IntSetting("-foo", "foo", 80) + val foo = IntSetting("", "-foo", "foo", 80) import Settings._ val args = List("-foo:100") @@ -181,10 +181,10 @@ class SettingsTests { @Test def `Set BooleanSettings correctly`: Unit = object Settings extends SettingGroup: - val foo = BooleanSetting("-foo", "foo", false) - val bar = BooleanSetting("-bar", "bar", true) - val baz = BooleanSetting("-baz", "baz", false) - val qux = BooleanSetting("-qux", "qux", false) + val foo = BooleanSetting("", "-foo", "foo", false) + val bar = BooleanSetting("", "-bar", "bar", true) + val baz = BooleanSetting("", "-baz", "baz", false) + val qux = BooleanSetting("", "-qux", "qux", false) import Settings._ val args = List("-foo:true", "-bar:false", "-baz", "-qux:true", "-qux:false") diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index a1014043724e..77691e1008e0 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -12,7 +12,7 @@ class ScalaSettingsTests: @Test def `A setting with aliases is accepted`: Unit = class MySettings extends SettingGroup: - val classpath: Setting[String] = PathSetting("-classpath", "Specify where to find user class files.", ".", aliases = List("--class-path", "-cp")) + val classpath: Setting[String] = PathSetting("", "classpath", "Specify where to find user class files.", ".", aliases = List("--class-path", "-cp")) val settings = MySettings() val args = tokenize("-cp path/to/classes1:other/path/to/classes2") val summary = ArgsSummary(settings.defaultState, args, errors = Nil, warnings = Nil) @@ -25,7 +25,7 @@ class ScalaSettingsTests: @Test def `A multistring setting is multivalued`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:implicitConversions,dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) @@ -39,7 +39,7 @@ class ScalaSettingsTests: @Test def `t9719 Apply -language more than once`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:implicitConversions -language:dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) @@ -53,7 +53,7 @@ class ScalaSettingsTests: @Test def `Warn if multistring element is supplied multiply`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:dynamics -language:implicitConversions -language:dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) @@ -67,7 +67,7 @@ class ScalaSettingsTests: @Test def `WConf setting is parsed`: Unit = import reporting.{Action, Diagnostic, NoExplanation} - val sets = new ScalaSettings + val sets = ScalaSettings val args = List("-Wconf:cat=deprecation:s,cat=feature:e", "-Wconf:msg=a problem\\.:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) @@ -85,7 +85,7 @@ class ScalaSettingsTests: @Test def `i18367 rightmost WConf flags take precedence over flags to the left`: Unit = import reporting.{Action, Diagnostic} - val sets = new ScalaSettings + val sets = ScalaSettings val args = List("-Wconf:cat=deprecation:e", "-Wconf:cat=deprecation:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) From 85d4a876d4908f50e412e7f9d4875fb6e34036f3 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 22 Feb 2024 18:15:56 +0100 Subject: [PATCH 02/15] Fix scaladoc --- .../tools/scaladoc/ScaladocSettings.scala | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index 4802f5d24ecc..5acfac03d52c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -2,6 +2,7 @@ package dotty.tools.scaladoc import dotty.tools.dotc.config.Settings._ import dotty.tools.dotc.config.AllScalaSettings +import dotty.tools.dotc.config.ScalaSettingCategories.RootSetting class ScaladocSettings extends SettingGroup with AllScalaSettings: val unsupportedSettings = Seq( @@ -11,90 +12,95 @@ class ScaladocSettings extends SettingGroup with AllScalaSettings: val projectName: Setting[String] = - StringSetting("-project", "project title", "The name of the project.", "", aliases = List("-doc-title")) + StringSetting(RootSetting, "project", "project title", "The name of the project.", "", aliases = List("-doc-title")) val projectVersion: Setting[String] = - StringSetting("-project-version", "project version", "The current version of your project.", "", aliases = List("-doc-version")) + StringSetting(RootSetting, "project-version", "project version", "The current version of your project.", "", aliases = List("-doc-version")) val projectLogo: Setting[String] = - StringSetting("-project-logo", "project logo filename", "Path to the file that contains the project's logo. Provided path can be absolute or relative to the project root directory.", "", aliases = List("-doc-logo")) + StringSetting(RootSetting, "project-logo", "project logo filename", "Path to the file that contains the project's logo. Provided path can be absolute or relative to the project root directory.", "", aliases = List("-doc-logo")) - val projectFooter: Setting[String] = StringSetting("-project-footer", "project footer", "A footer on every Scaladoc page.", "", aliases = List("-doc-footer")) + val projectFooter: Setting[String] = StringSetting(RootSetting, "project-footer", "project footer", "A footer on every Scaladoc page.", "", aliases = List("-doc-footer")) val sourceLinks: Setting[List[String]] = - MultiStringSetting("-source-links", "sources", SourceLinks.usage) + MultiStringSetting(RootSetting, "source-links", "sources", SourceLinks.usage) val legacySourceLink: Setting[String] = - StringSetting("-doc-source-url", "sources", "Legacy option from Scala 2. Use -source-links instead.", "") + StringSetting(RootSetting, "doc-source-url", "sources", "Legacy option from Scala 2. Use -source-links instead.", "") val syntax: Setting[List[String]] = - MultiStringSetting("-comment-syntax", "syntax", tasty.comments.CommentSyntaxArgs.usage) + MultiStringSetting(RootSetting, "comment-syntax", "syntax", tasty.comments.CommentSyntaxArgs.usage) val revision: Setting[String] = - StringSetting("-revision", "revision", "Revision (branch or ref) used to build project project", "") + StringSetting(RootSetting, "revision", "revision", "Revision (branch or ref) used to build project project", "") val externalDocumentationMappings: Setting[List[String]] = - MultiStringSetting("-external-mappings", "external-mappings", + MultiStringSetting(RootSetting, "external-mappings", "external-mappings", "Mapping between regexes matching classpath entries and external documentation. " + "'regex::[scaladoc|scaladoc|javadoc]::path' syntax is used") val legacyExternalDocumentationMappings: Setting[List[String]] = - MultiStringSetting("-doc-external-doc", "legacy-external-mappings", "Legacy option from Scala 2. Mapping betweeen path and external documentation. Use -external-mappings instead.") + MultiStringSetting(RootSetting, "doc-external-doc", "legacy-external-mappings", "Legacy option from Scala 2. Mapping betweeen path and external documentation. Use -external-mappings instead.") val socialLinks: Setting[List[String]] = - MultiStringSetting("-social-links", "social-links", + MultiStringSetting(RootSetting, "social-links", "social-links", "Links to social sites. '[github|twitter|gitter|discord]::link' or 'custom::link::light_icon_file_name[::dark_icon_file_name]' syntax is used. For custom links, the icons must be present in '_assets/images/'") val deprecatedSkipPackages: Setting[List[String]] = - MultiStringSetting("-skip-packages", "packages", "Deprecated, please use `-skip-by-id` or `-skip-by-regex`") + MultiStringSetting(RootSetting, "skip-packages", "packages", "Deprecated, please use `-skip-by-id` or `-skip-by-regex`") val skipById: Setting[List[String]] = - MultiStringSetting("-skip-by-id", "package or class identifier", "Identifiers of packages or top-level classes to skip when generating documentation") + MultiStringSetting(RootSetting, "skip-by-id", "package or class identifier", "Identifiers of packages or top-level classes to skip when generating documentation") val skipByRegex: Setting[List[String]] = - MultiStringSetting("-skip-by-regex", "regex", "Regexes that match fully qualified names of packages or top-level classes to skip when generating documentation") + MultiStringSetting(RootSetting, "skip-by-regex", "regex", "Regexes that match fully qualified names of packages or top-level classes to skip when generating documentation") val docRootContent: Setting[String] = - StringSetting("-doc-root-content", "path", "The file from which the root package documentation should be imported.", "") + StringSetting(RootSetting, "doc-root-content", "path", "The file from which the root package documentation should be imported.", "") val author: Setting[Boolean] = - BooleanSetting("-author", "Include authors.", false) + BooleanSetting(RootSetting, "author", "Include authors.", false) val groups: Setting[Boolean] = - BooleanSetting("-groups", "Group similar functions together (based on the @group annotation)", false) + BooleanSetting(RootSetting, "groups", "Group similar functions together (based on the @group annotation)", false) val visibilityPrivate: Setting[Boolean] = - BooleanSetting("-private", "Show all types and members. Unless specified, show only public and protected types and members.", false) + BooleanSetting(RootSetting, "private", "Show all types and members. Unless specified, show only public and protected types and members.", false) val docCanonicalBaseUrl: Setting[String] = StringSetting( - "-doc-canonical-base-url", + RootSetting, + "doc-canonical-base-url", "url", s"A base URL to use as prefix and add `canonical` URLs to all pages. The canonical URL may be used by search engines to choose the URL that you want people to see in search results. If unset no canonical URLs are generated.", "" ) val siteRoot: Setting[String] = StringSetting( - "-siteroot", + RootSetting, + "siteroot", "site root", "A directory containing static files from which to generate documentation.", "./docs" ) val noLinkWarnings: Setting[Boolean] = BooleanSetting( - "-no-link-warnings", + RootSetting, + "no-link-warnings", "Avoid warnings for ambiguous and incorrect links in members look up. Doesn't affect warnings for incorrect links of assets etc.", false ) val noLinkAssetWarnings: Setting[Boolean] = BooleanSetting( - "-no-link-asset-warnings", + RootSetting, + "no-link-asset-warnings", "Avoid warnings for incorrect links of assets like images, static pages, etc.", false ) val versionsDictionaryUrl: Setting[String] = StringSetting( - "-versions-dictionary-url", + RootSetting, + "versions-dictionary-url", "versions dictionary url", "A URL pointing to a JSON document containing a dictionary `version label -> documentation location`. " + "The JSON file has single property \"versions\" that holds dictionary of labels of specific docs and URL pointing to their index.html top-level file. " + @@ -103,23 +109,24 @@ class ScaladocSettings extends SettingGroup with AllScalaSettings: ) val YdocumentSyntheticTypes: Setting[Boolean] = - BooleanSetting("-Ydocument-synthetic-types", "Attach pages with documentation of the intrinsic types e. g. Any, Nothing to the docs. Setting is useful only for stdlib.", false) + BooleanSetting(RootSetting, "Ydocument-synthetic-types", "Attach pages with documentation of the intrinsic types e. g. Any, Nothing to the docs. Setting is useful only for stdlib.", false) val snippetCompiler: Setting[List[String]] = - MultiStringSetting("-snippet-compiler", "snippet-compiler", snippets.SnippetCompilerArgs.usage) + MultiStringSetting(RootSetting, "snippet-compiler", "snippet-compiler", snippets.SnippetCompilerArgs.usage) val generateInkuire: Setting[Boolean] = - BooleanSetting("-Ygenerate-inkuire", "Generates InkuireDB and enables Hoogle-like searches", false) + BooleanSetting(RootSetting, "Ygenerate-inkuire", "Generates InkuireDB and enables Hoogle-like searches", false) val apiSubdirectory: Setting[Boolean] = - BooleanSetting("-Yapi-subdirectory", "Put the API documentation pages inside a directory `api/`", false) + BooleanSetting(RootSetting, "Yapi-subdirectory", "Put the API documentation pages inside a directory `api/`", false) val scastieConfiguration: Setting[String] = - StringSetting("-scastie-configuration", "Scastie configuration", "Additional configuration passed to Scastie in code snippets", "") + StringSetting(RootSetting, "scastie-configuration", "Scastie configuration", "Additional configuration passed to Scastie in code snippets", "") val defaultTemplate: Setting[String] = StringSetting( - "-default-template", + RootSetting, + "default-template", "default template used by static site", "The static site is generating empty files for indexes that haven't been provided explicitly in a sidebar/missing index.html in directory. " + "User can specify what default template should be used for such indexes. It can be useful for providing generic templates that interpolate some common settings, like title, or can have some custom html embedded.", @@ -128,13 +135,14 @@ class ScaladocSettings extends SettingGroup with AllScalaSettings: val quickLinks: Setting[List[String]] = MultiStringSetting( - "-quick-links", + RootSetting, + "quick-links", "quick-links", "List of quick links that is displayed in the header of documentation." ) val dynamicSideMenu: Setting[Boolean] = - BooleanSetting("-dynamic-side-menu", "Generate side menu via JS instead of embedding it in every html file", false) + BooleanSetting(RootSetting, "dynamic-side-menu", "Generate side menu via JS instead of embedding it in every html file", false) def scaladocSpecificSettings: Set[Setting[?]] = Set(sourceLinks, legacySourceLink, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent, snippetCompiler, generateInkuire, defaultTemplate, scastieConfiguration, quickLinks, dynamicSideMenu) From 03c695d1c7099de6f9a2204faa77b7303fc61b66 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 23 Feb 2024 13:27:26 +0100 Subject: [PATCH 03/15] More unification, fixed test --- .../dotty/tools/dotc/config/Settings.scala | 16 +++++++++++++- .../test/dotty/tools/dotc/SettingsTests.scala | 22 +++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 961bca4da121..af160a8bf1fe 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -170,6 +170,15 @@ object Settings: case (StringTag, arg2 :: args2) => if (arg2 startsWith "-") missingArg else setString(arg2, args2) + case (OutputTag, _) if argRest.nonEmpty => + val path = Directory(argRest) + val isJar = path.extension == "jar" + if (!isJar && !path.isDirectory) + fail(s"'$argRest' does not exist or is not a directory or .jar file", args) + else { + val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) + update(output, args) + } case (OutputTag, arg :: args) => val path = Directory(arg) val isJar = path.extension == "jar" @@ -183,11 +192,16 @@ object Settings: setInt(argRest, args) case (IntTag, arg2 :: args2) => setInt(arg2, args2) - case (VersionTag, _) => + case (VersionTag, _) if argRest.nonEmpty => ScalaVersion.parse(argRest) match { case Success(v) => update(v, args) case Failure(ex) => fail(ex.getMessage, args) } + case (VersionTag, arg2 :: args2) => + ScalaVersion.parse(arg2) match { + case Success(v) => update(v, args2) + case Failure(ex) => fail(ex.getMessage, args2) + } case (_, Nil) => missingArg } diff --git a/compiler/test/dotty/tools/dotc/SettingsTests.scala b/compiler/test/dotty/tools/dotc/SettingsTests.scala index 416b3fcc8b91..ab9ac00099c3 100644 --- a/compiler/test/dotty/tools/dotc/SettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/SettingsTests.scala @@ -87,7 +87,7 @@ class SettingsTests { @Test def `bad option warning consumes an arg`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "-option", "Some option") + val option = BooleanSetting("", "option", "Some option") val args = List("-adoption", "dogs", "cats") val summary = Settings.processArguments(args, processAll = true) @@ -97,7 +97,7 @@ class SettingsTests { @Test def `bad option settings throws`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "-option", "Some option") + val option = BooleanSetting("", "option", "Some option") def checkMessage(s: String): (Throwable => Boolean) = t => if t.getMessage == s then true @@ -112,12 +112,12 @@ class SettingsTests { @Test def validateChoices: Unit = object Settings extends SettingGroup: - val foo = ChoiceSetting("", "-foo", "foo", "Foo", List("a", "b"), "a") - val bar = IntChoiceSetting("", "-bar", "Bar", List(0, 1, 2), 0) - val baz = IntChoiceSetting("", "-baz", "Baz", 0 to 10, 10) + val foo = ChoiceSetting("", "foo", "foo", "Foo", List("a", "b"), "a") + val bar = IntChoiceSetting("", "bar", "Bar", List(0, 1, 2), 0) + val baz = IntChoiceSetting("", "baz", "Baz", 0 to 10, 10) - val quux = ChoiceSetting("", "-quux", "quux", "Quux", List(), "") - val quuz = IntChoiceSetting("", "-quuz", "Quuz", List(), 0) + val quux = ChoiceSetting("", "quux", "quux", "Quux", List(), "") + val quuz = IntChoiceSetting("", "quuz", "Quuz", List(), 0) locally { val args = List("-foo", "b", "-bar", "1", "-baz", "5") @@ -181,10 +181,10 @@ class SettingsTests { @Test def `Set BooleanSettings correctly`: Unit = object Settings extends SettingGroup: - val foo = BooleanSetting("", "-foo", "foo", false) - val bar = BooleanSetting("", "-bar", "bar", true) - val baz = BooleanSetting("", "-baz", "baz", false) - val qux = BooleanSetting("", "-qux", "qux", false) + val foo = BooleanSetting("", "foo", "foo", false) + val bar = BooleanSetting("", "bar", "bar", true) + val baz = BooleanSetting("", "baz", "baz", false) + val qux = BooleanSetting("", "qux", "qux", false) import Settings._ val args = List("-foo:true", "-bar:false", "-baz", "-qux:true", "-qux:false") From 748073bac7b55ebcc8a052a585657da8ab5562e2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 29 Feb 2024 15:34:25 +0100 Subject: [PATCH 04/15] Keep ScalaSettings as a class --- compiler/src/dotty/tools/dotc/Driver.scala | 1 - .../tools/dotc/config/CompilerCommand.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 27 ++++++++++--------- .../src/dotty/tools/dotc/core/Contexts.scala | 4 +-- .../dotty/tools/dotc/ScalaCommandTest.scala | 4 +-- .../tools/dotc/TastyBootstrapTests.scala | 2 +- .../dotc/config/ScalaSettingsTests.scala | 4 +-- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 82a9f5850bb6..6b461cc8e81b 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -200,7 +200,6 @@ class Driver { } def main(args: Array[String]): Unit = { - println("settings: " + ScalaSettings.allSettings) // Preload scala.util.control.NonFatal. Otherwise, when trying to catch a StackOverflowError, // we may try to load it but fail with another StackOverflowError and lose the original exception, // see . diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 2e82c6a3cd67..587f94dad7b3 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -5,7 +5,7 @@ import Settings.* import core.Contexts.* abstract class CompilerCommand extends CliCommand: - type ConcreteSettings = ScalaSettings.type + type ConcreteSettings = ScalaSettings final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 92150ae6e426..f5b2fd6e1f6a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -23,19 +23,7 @@ object ScalaSettingCategories: val AdvancedSetting = "X" val VerboseSetting = "V" -object ScalaSettings extends SettingGroup with AllScalaSettings: - - val settingsByCategory: Map[String, List[Setting[_]]] = - allSettings.groupBy(_.category) - .view.mapValues(_.toList).toMap - .withDefaultValue(Nil) - def categories: List[String] = settingsByCategory.keys.toList - val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) - val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) - val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) - val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) - val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) - val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap +object ScalaSettings: private lazy val minTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).min private lazy val maxTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).max @@ -69,6 +57,19 @@ object ScalaSettings extends SettingGroup with AllScalaSettings: else defaultWidth else defaultWidth } + +class ScalaSettings extends SettingGroup, AllScalaSettings: + val settingsByCategory: Map[String, List[Setting[_]]] = + allSettings.groupBy(_.category) + .view.mapValues(_.toList).toMap + .withDefaultValue(Nil) + def categories: List[String] = settingsByCategory.keys.toList + val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) + val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) + val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) + val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) + val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) + val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSettings, WarningSettings, XSettings, YSettings: self: SettingGroup => diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b94824ac3a2b..708852076279 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -513,7 +513,7 @@ object Contexts { s"""Context( |${outersIterator.map(ctx => cinfo(using ctx)).mkString("\n\n")})""".stripMargin - def settings: ScalaSettings.type = base.settings + def settings: ScalaSettings = base.settings def definitions: Definitions = base.definitions def platform: Platform = base.platform def pendingUnderlying: util.HashSet[Type] = base.pendingUnderlying @@ -861,7 +861,7 @@ object Contexts { with Phases.PhasesBase with Plugins { - val settings: ScalaSettings.type = ScalaSettings + val settings: ScalaSettings = new ScalaSettings() /** The initial context */ val initialCtx: Context = FreshContext.initial(this: @unchecked, settings) diff --git a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala index 852a232e4c6a..9f51a3b27baa 100644 --- a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala +++ b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala @@ -17,7 +17,7 @@ class ScalaCommandTest: def temporaryFolder = _temporaryFolder @Test def `Simple one parameter`: Unit = inContext { - val settings = config.ScalaSettings + val settings = new config.ScalaSettings() val args = "-cp path/to/classes1:other/path/to/classes2 files".split(" ") val summary = ScalacCommand.distill(args, settings)() given SettingsState = summary.sstate @@ -26,7 +26,7 @@ class ScalaCommandTest: } @Test def `Unfold @file`: Unit = inContext { - val settings = config.ScalaSettings + val settings = new config.ScalaSettings() val file = temporaryFolder.newFile("config") val writer = java.io.FileWriter(file); writer.write("-sourceroot myNewRoot someMoreFiles"); diff --git a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala index 27311497de9c..9929e1e87214 100644 --- a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala +++ b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala @@ -51,7 +51,7 @@ class TastyBootstrapTests { Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm, Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader, ).mkString(File.pathSeparator), - Array("-Ycheck-reentrant", "-language:postfixOps", "-Xsemanticdb") + Array("-Ycheck-reentrant", "-Ylog:checkReentrant+", "-language:postfixOps", "-Xsemanticdb") ) val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped")) diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 77691e1008e0..fe566e1a4de3 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -67,7 +67,7 @@ class ScalaSettingsTests: @Test def `WConf setting is parsed`: Unit = import reporting.{Action, Diagnostic, NoExplanation} - val sets = ScalaSettings + val sets = new ScalaSettings() val args = List("-Wconf:cat=deprecation:s,cat=feature:e", "-Wconf:msg=a problem\\.:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) @@ -85,7 +85,7 @@ class ScalaSettingsTests: @Test def `i18367 rightmost WConf flags take precedence over flags to the left`: Unit = import reporting.{Action, Diagnostic} - val sets = ScalaSettings + val sets = new ScalaSettings() val args = List("-Wconf:cat=deprecation:e", "-Wconf:cat=deprecation:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) From 22fe3534c4b7abe47c236cb7b86b59347a952939 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 1 Mar 2024 14:19:40 +0100 Subject: [PATCH 05/15] Fix SettingsTests --- compiler/test/dotty/tools/dotc/SettingsTests.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/SettingsTests.scala b/compiler/test/dotty/tools/dotc/SettingsTests.scala index ab9ac00099c3..595c4875bb12 100644 --- a/compiler/test/dotty/tools/dotc/SettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/SettingsTests.scala @@ -72,7 +72,7 @@ class SettingsTests { @Test def `dont crash on many options`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "-option", "Some option") + val option = BooleanSetting("", "option", "Some option") val limit = 6000 val args = List.fill(limit)("-option") @@ -169,7 +169,7 @@ class SettingsTests { @Test def `Allow IntSetting's to be set with a colon`: Unit = object Settings extends SettingGroup: - val foo = IntSetting("", "-foo", "foo", 80) + val foo = IntSetting("", "foo", "foo", 80) import Settings._ val args = List("-foo:100") From 97f0c1d308b4070690f5a7d0b3999d7d884aec6d Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 4 Mar 2024 13:58:40 +0100 Subject: [PATCH 06/15] Revert object state & mark as unshared --- .../tools/dotc/config/ScalaSettings.scala | 28 +++++++++---------- .../dotty/tools/dotc/config/Settings.scala | 3 ++ .../src/dotty/tools/dotc/core/Contexts.scala | 2 +- .../dotty/tools/dotc/ScalaCommandTest.scala | 4 +-- .../dotc/config/ScalaSettingsTests.scala | 4 +-- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index f5b2fd6e1f6a..33311aa23291 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -23,7 +23,20 @@ object ScalaSettingCategories: val AdvancedSetting = "X" val VerboseSetting = "V" -object ScalaSettings: +type ScalaSettings = ScalaSettings.type + +object ScalaSettings extends SettingGroup, AllScalaSettings: + val settingsByCategory: Map[String, List[Setting[_]]] = + allSettings.groupBy(_.category) + .view.mapValues(_.toList).toMap + .withDefaultValue(Nil) + def categories: List[String] = settingsByCategory.keys.toList + val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) + val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) + val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) + val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) + val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) + val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap private lazy val minTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).min private lazy val maxTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).max @@ -58,19 +71,6 @@ object ScalaSettings: else defaultWidth } -class ScalaSettings extends SettingGroup, AllScalaSettings: - val settingsByCategory: Map[String, List[Setting[_]]] = - allSettings.groupBy(_.category) - .view.mapValues(_.toList).toMap - .withDefaultValue(Nil) - def categories: List[String] = settingsByCategory.keys.toList - val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) - val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) - val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) - val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) - val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) - val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap - trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSettings, WarningSettings, XSettings, YSettings: self: SettingGroup => diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index af160a8bf1fe..16af5468ccf0 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -8,6 +8,7 @@ import core.Contexts.* import dotty.tools.io.{AbstractFile, Directory, JarArchive, PlainDirectory} import annotation.tailrec +import annotation.internal.unshared import collection.mutable.ArrayBuffer import collection.mutable import reflect.ClassTag @@ -240,6 +241,7 @@ object Settings: class SettingGroup { + @unshared private val _allSettings = new ArrayBuffer[Setting[?]] def allSettings: Seq[Setting[?]] = _allSettings.toSeq @@ -307,6 +309,7 @@ object Settings: setting } + @unshared val settingCharacters = "[a-zA-Z0-9_\\-]*".r def validateSetting(setting: String): String = assert(settingCharacters.matches(setting), s"Setting $setting contains invalid characters") diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 708852076279..ae21c6fb8763 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -861,7 +861,7 @@ object Contexts { with Phases.PhasesBase with Plugins { - val settings: ScalaSettings = new ScalaSettings() + val settings: ScalaSettings = ScalaSettings /** The initial context */ val initialCtx: Context = FreshContext.initial(this: @unchecked, settings) diff --git a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala index 9f51a3b27baa..852a232e4c6a 100644 --- a/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala +++ b/compiler/test/dotty/tools/dotc/ScalaCommandTest.scala @@ -17,7 +17,7 @@ class ScalaCommandTest: def temporaryFolder = _temporaryFolder @Test def `Simple one parameter`: Unit = inContext { - val settings = new config.ScalaSettings() + val settings = config.ScalaSettings val args = "-cp path/to/classes1:other/path/to/classes2 files".split(" ") val summary = ScalacCommand.distill(args, settings)() given SettingsState = summary.sstate @@ -26,7 +26,7 @@ class ScalaCommandTest: } @Test def `Unfold @file`: Unit = inContext { - val settings = new config.ScalaSettings() + val settings = config.ScalaSettings val file = temporaryFolder.newFile("config") val writer = java.io.FileWriter(file); writer.write("-sourceroot myNewRoot someMoreFiles"); diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index fe566e1a4de3..77691e1008e0 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -67,7 +67,7 @@ class ScalaSettingsTests: @Test def `WConf setting is parsed`: Unit = import reporting.{Action, Diagnostic, NoExplanation} - val sets = new ScalaSettings() + val sets = ScalaSettings val args = List("-Wconf:cat=deprecation:s,cat=feature:e", "-Wconf:msg=a problem\\.:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) @@ -85,7 +85,7 @@ class ScalaSettingsTests: @Test def `i18367 rightmost WConf flags take precedence over flags to the left`: Unit = import reporting.{Action, Diagnostic} - val sets = new ScalaSettings() + val sets = ScalaSettings val args = List("-Wconf:cat=deprecation:e", "-Wconf:cat=deprecation:s") val sumy = ArgsSummary(sets.defaultState, args, errors = Nil, warnings = Nil) val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) From f885c41c5f32568ed6dcb7d51b0269255a011482 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 4 Mar 2024 15:23:16 +0100 Subject: [PATCH 07/15] Remove ScalaSettings type: --- compiler/src/dotty/tools/dotc/config/CompilerCommand.scala | 2 +- compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 2 -- compiler/src/dotty/tools/dotc/core/Contexts.scala | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 587f94dad7b3..2e82c6a3cd67 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -5,7 +5,7 @@ import Settings.* import core.Contexts.* abstract class CompilerCommand extends CliCommand: - type ConcreteSettings = ScalaSettings + type ConcreteSettings = ScalaSettings.type final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 33311aa23291..de3fa70d89e4 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -23,8 +23,6 @@ object ScalaSettingCategories: val AdvancedSetting = "X" val VerboseSetting = "V" -type ScalaSettings = ScalaSettings.type - object ScalaSettings extends SettingGroup, AllScalaSettings: val settingsByCategory: Map[String, List[Setting[_]]] = allSettings.groupBy(_.category) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index ae21c6fb8763..b94824ac3a2b 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -513,7 +513,7 @@ object Contexts { s"""Context( |${outersIterator.map(ctx => cinfo(using ctx)).mkString("\n\n")})""".stripMargin - def settings: ScalaSettings = base.settings + def settings: ScalaSettings.type = base.settings def definitions: Definitions = base.definitions def platform: Platform = base.platform def pendingUnderlying: util.HashSet[Type] = base.pendingUnderlying @@ -861,7 +861,7 @@ object Contexts { with Phases.PhasesBase with Plugins { - val settings: ScalaSettings = ScalaSettings + val settings: ScalaSettings.type = ScalaSettings /** The initial context */ val initialCtx: Context = FreshContext.initial(this: @unchecked, settings) From 7a2a24aaf78165089d46cf043f1a343a14bed7b4 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 4 Mar 2024 17:49:49 +0100 Subject: [PATCH 08/15] Changes in parameter parsing & deprecation of -Xlint --- .../dotty/tools/dotc/config/CliCommand.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 14 ++-- .../dotty/tools/dotc/config/Settings.scala | 72 ++++++++++--------- .../tools/dotc/transform/CheckShadowing.scala | 6 +- .../dotty/tools/dotc/CompilationTests.scala | 2 +- tests/neg/i17612b/i17612b.scala | 2 +- tests/neg/i17613b/i17613b.scala | 2 +- tests/warn/i17612a.scala | 2 +- tests/warn/i17613a.scala | 2 +- 9 files changed, 58 insertions(+), 46 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/CliCommand.scala b/compiler/src/dotty/tools/dotc/config/CliCommand.scala index 5c24dd57eeba..be97297218fa 100644 --- a/compiler/src/dotty/tools/dotc/config/CliCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CliCommand.scala @@ -86,7 +86,7 @@ trait CliCommand: protected def isVerbose(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = s.name.startsWith("-V") && s.name != "-V" protected def isWarning(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = - s.name.startsWith("-W") && s.name != "-W" || s.name == "-Xlint" + s.name.startsWith("-W") && s.name != "-W" protected def isAdvanced(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = s.name.startsWith("-X") && s.name != "-X" protected def isPrivate(s: Setting[?])(using settings: ConcreteSettings)(using SettingsState): Boolean = diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index de3fa70d89e4..1c7554dcd74e 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -296,9 +296,9 @@ private sealed trait WarningSettings: |to prevent the shell from expanding patterns.""".stripMargin, ) - val Xlint: Setting[List[ChoiceWithHelp[String]]] = UncompleteMultiChoiceHelpSetting( - AdvancedSetting, - name = "Xlint", + val Wshadow: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + WarningSetting, + name = "Wshadow", helpArg = "advanced warning", descr = "Enable or disable specific `lint` warnings", choices = List( @@ -309,9 +309,9 @@ private sealed trait WarningSettings: default = Nil ) - object XlintHas: + object WshadowHas: def allOr(s: String)(using Context) = - Xlint.value.pipe(us => us.contains("all") || us.contains(s)) + Wshadow.value.pipe(us => us.contains("all") || us.contains(s)) def privateShadow(using Context) = allOr("private-shadow") def typeParameterShadow(using Context) = @@ -357,6 +357,10 @@ private sealed trait XSettings: } val XmacroSettings: Setting[List[String]] = MultiStringSetting(AdvancedSetting, "Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") + + // Deprecated + val Xlint: Setting[_] = DeprecatedSetting(AdvancedSetting, "Xlint", "Enable or disable specific warnings", "Use -Wshadow to enable shadowing lints or -W: to enable specific sets of warnings.") + end XSettings /** -Y "Forking" as in forked tongue or "Private" settings */ diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 16af5468ccf0..c352d2cb9cbb 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -70,10 +70,15 @@ object Settings: aliases: List[String] = Nil, depends: List[(Setting[?], Any)] = Nil, ignoreInvalidArgs: Boolean = false, - propertyClass: Option[Class[?]] = None)(private[Settings] val idx: Int) { + propertyClass: Option[Class[?]] = None, + deprecationMsg: Option[String] = None)(private[Settings] val idx: Int) { assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") + // 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") + val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T] @@ -150,6 +155,24 @@ object Settings: update(x, args) catch case _: NumberFormatException => fail(s"$argValue is not an integer argument for $name", args) + + def setOutput(argValue: String, args: List[String]) = + val path = Directory(argValue) + val isJar = path.extension == "jar" + if (!isJar && !path.isDirectory) + fail(s"'$argValue' does not exist or is not a directory or .jar file", args) + else { + val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) + update(output, args) + } + + def appendList(strings: List[String], args: List[String]) = + choices match + case Some(valid) => strings.filterNot(valid.contains) match + case Nil => update(strings, args) + case invalid => invalidChoices(invalid) + case _ => update(strings, args) + def doSet(argRest: String) = ((summon[ClassTag[T]], args): @unchecked) match { @@ -157,38 +180,20 @@ object Settings: setBoolean(argRest, args) case (OptionTag, _) => update(Some(propertyClass.get.getConstructor().newInstance()), args) - case (ListTag, _) => - if (argRest.isEmpty) missingArg - else - val strings = argRest.split(",").toList - choices match - case Some(valid) => strings.filterNot(valid.contains) match - case Nil => update(strings, args) - case invalid => invalidChoices(invalid) - case _ => update(strings, args) + case (ListTag, args) if argRest.nonEmpty => + val strings = argRest.split(",").toList + appendList(strings, args) + case (ListTag, arg2 :: args2) if !(arg2 startsWith "-")=> + appendList(arg2 :: Nil, args2) case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => setString(argRest, args) case (StringTag, arg2 :: args2) => if (arg2 startsWith "-") missingArg else setString(arg2, args2) - case (OutputTag, _) if argRest.nonEmpty => - val path = Directory(argRest) - val isJar = path.extension == "jar" - if (!isJar && !path.isDirectory) - fail(s"'$argRest' does not exist or is not a directory or .jar file", args) - else { - val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) - update(output, args) - } - case (OutputTag, arg :: args) => - val path = Directory(arg) - val isJar = path.extension == "jar" - if (!isJar && !path.isDirectory) - fail(s"'$arg' does not exist or is not a directory or .jar file", args) - else { - val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) - update(output, args) - } + case (OutputTag, args) if argRest.nonEmpty => + setOutput(argRest, args) + case (OutputTag, arg2 :: args2) => + setOutput(arg2, args2) case (IntTag, args) if argRest.nonEmpty => setInt(argRest, args) case (IntTag, arg2 :: args2) => @@ -214,7 +219,10 @@ object Settings: if(prefix.isEmpty) arg.dropWhile(_ != ':').drop(1) else arg.drop(prefix.get.length) if matches(arg) then - doSet(argValRest) + if deprecationMsg.isDefined then + warn(s"Option $name is deprecated: ${deprecationMsg.get}", args) + else + doSet(argValRest) else state } @@ -334,9 +342,6 @@ object Settings: def MultiChoiceHelpSetting(category: String, 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, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) - def UncompleteMultiChoiceHelpSetting(category: String, 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, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting), ignoreInvalidArgs = true)) - def IntSetting(category: String, name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = publish(Setting(category, validateAndPrependName(name), descr, default, aliases = aliases.map(validateSetting))) @@ -364,5 +369,8 @@ object Settings: def OptionSetting[T: ClassTag](category: String, name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = publish(Setting(category, validateAndPrependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases.map(validateSetting))) + + def DeprecatedSetting(category: String, name: String, descr: String, deprecationMsg: String): Setting[Boolean] = + publish(Setting(category, validateAndPrependName(name), descr, false, deprecationMsg = Some(deprecationMsg))) } end Settings diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index a85cabdd5460..87d652bd9133 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -51,7 +51,7 @@ class CheckShadowing extends MiniPhase: override def isRunnable(using Context): Boolean = super.isRunnable && - ctx.settings.Xlint.value.nonEmpty && + ctx.settings.Wshadow.value.nonEmpty && !ctx.isJava // Setup before the traversal @@ -266,12 +266,12 @@ object CheckShadowing: /** Get the shadowing analysis's result */ def getShadowingResult(using Context): ShadowResult = val privateWarnings: List[ShadowWarning] = - if ctx.settings.XlintHas.privateShadow then + if ctx.settings.WshadowHas.privateShadow then privateShadowWarnings.toList else Nil val typeParamWarnings: List[ShadowWarning] = - if ctx.settings.XlintHas.typeParameterShadow then + if ctx.settings.WshadowHas.typeParameterShadow then typeParamShadowWarnings.toList else Nil diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 64c2bd3b8807..a96a4ea09102 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -31,7 +31,7 @@ class CompilationTests { @Test def pos: Unit = { implicit val testGroup: TestGroup = TestGroup("compilePos") var tests = List( - compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init", "-Wunused:all", "-Xlint:private-shadow", "-Xlint:type-parameter-shadow"), FileFilter.include(TestSources.posLintingAllowlist)), + compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init", "-Wunused:all", "-Wshadow:private-shadow", "-Wshadow:type-parameter-shadow"), FileFilter.include(TestSources.posLintingAllowlist)), compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init"), FileFilter.exclude(TestSources.posLintingAllowlist)), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), diff --git a/tests/neg/i17612b/i17612b.scala b/tests/neg/i17612b/i17612b.scala index 62d6ad17ff3d..a7afbf019d07 100644 --- a/tests/neg/i17612b/i17612b.scala +++ b/tests/neg/i17612b/i17612b.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -Xlint:private-shadow -source:3.3 +//> using options -Xfatal-warnings -Wshadow:private-shadow -source:3.3 object i17612b: diff --git a/tests/neg/i17613b/i17613b.scala b/tests/neg/i17613b/i17613b.scala index 42506a274a65..0969a9a869ea 100644 --- a/tests/neg/i17613b/i17613b.scala +++ b/tests/neg/i17613b/i17613b.scala @@ -1,4 +1,4 @@ -//> using options -Xlint:type-parameter-shadow -Xfatal-warnings +//> using options -Wshadow:type-parameter-shadow -Xfatal-warnings object i17613b: import importTry._ diff --git a/tests/warn/i17612a.scala b/tests/warn/i17612a.scala index 7473d8fd9ec9..87fddf9c180f 100644 --- a/tests/warn/i17612a.scala +++ b/tests/warn/i17612a.scala @@ -1,4 +1,4 @@ -//> using options -Xlint:private-shadow -source:3.3 +//> using options -Wshadow:private-shadow -source:3.3 object i17612a: class Base(var x: Int, val y: Int, var z: Int): diff --git a/tests/warn/i17613a.scala b/tests/warn/i17613a.scala index 6ee55a5cf973..5e841a79d105 100644 --- a/tests/warn/i17613a.scala +++ b/tests/warn/i17613a.scala @@ -1,4 +1,4 @@ -//> using options -Xlint:type-parameter-shadow +//> using options -Wshadow:type-parameter-shadow object i17613a: class B: From 55e4477350d2fe80db12139584515f634ed33dc2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 5 Mar 2024 12:50:24 +0100 Subject: [PATCH 09/15] Unify settings parsing --- .../tools/dotc/config/ScalaSettings.scala | 2 +- .../dotty/tools/dotc/config/Settings.scala | 64 +++++++++---------- tests/neg/kind-projector.scala | 2 +- .../dotc/config/ScalaSettings.scala | 2 +- tests/pos/kind-projector.scala | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 1c7554dcd74e..835f5d5a6668 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -401,7 +401,7 @@ private sealed trait YSettings: val YstopBefore: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") val YdetailedStats: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") - val YkindProjector: Setting[String] = ChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") + val YkindProjector: Setting[String] = ChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "enable", "underscores"), "disable") val YprintPos: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos", "Show tree positions.") val YprintPosSyms: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos-syms", "Show symbol definitions positions.") val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index c352d2cb9cbb..7c687eb5988b 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -72,9 +72,10 @@ object Settings: ignoreInvalidArgs: Boolean = false, propertyClass: Option[Class[?]] = None, deprecationMsg: Option[String] = None)(private[Settings] val idx: Int) { - + + assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") - + assert(!choices.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") @@ -165,6 +166,12 @@ object Settings: val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path) update(output, args) } + + def setVersion(argValue: String, args: List[String]) = + ScalaVersion.parse(argValue) match { + case Success(v) => update(v, args) + case Failure(ex) => fail(ex.getMessage, args) + } def appendList(strings: List[String], args: List[String]) = choices match @@ -180,38 +187,31 @@ object Settings: setBoolean(argRest, args) case (OptionTag, _) => update(Some(propertyClass.get.getConstructor().newInstance()), args) - case (ListTag, args) if argRest.nonEmpty => - val strings = argRest.split(",").toList - appendList(strings, args) - case (ListTag, arg2 :: args2) if !(arg2 startsWith "-")=> - appendList(arg2 :: Nil, args2) - case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => - setString(argRest, args) - case (StringTag, arg2 :: args2) => - if (arg2 startsWith "-") missingArg - else setString(arg2, args2) - case (OutputTag, args) if argRest.nonEmpty => - setOutput(argRest, args) - case (OutputTag, arg2 :: args2) => - setOutput(arg2, args2) - case (IntTag, args) if argRest.nonEmpty => - setInt(argRest, args) - case (IntTag, arg2 :: args2) => - setInt(arg2, args2) - case (VersionTag, _) if argRest.nonEmpty => - ScalaVersion.parse(argRest) match { - case Success(v) => update(v, args) - case Failure(ex) => fail(ex.getMessage, args) - } - case (VersionTag, arg2 :: args2) => - ScalaVersion.parse(arg2) match { - case Success(v) => update(v, args2) - case Failure(ex) => fail(ex.getMessage, args2) - } - case (_, Nil) => - missingArg + case (_, args) => + val argInArgRest = !argRest.isEmpty + val argAfterParam = !argInArgRest && args.nonEmpty && !args.head.startsWith("-") + if argInArgRest then + doSetArg(argRest, args) + else if argAfterParam then + doSetArg(args.head, args.tail) + else missingArg } + def doSetArg(arg: String, argsLeft: List[String]) = summon[ClassTag[T]] match + case ListTag => + val strings = arg.split(",").toList + appendList(strings, argsLeft) + case StringTag => + setString(arg, argsLeft) + case OutputTag => + setOutput(arg, argsLeft) + case IntTag => + setInt(arg, argsLeft) + case VersionTag => + setVersion(arg, argsLeft) + case _ => + missingArg + def matches(argName: String): Boolean = (allFullNames).exists(_ == argName.takeWhile(_ != ':')) || prefix.exists(arg.startsWith) diff --git a/tests/neg/kind-projector.scala b/tests/neg/kind-projector.scala index a7fc24c70b93..fa2bd20d60ce 100644 --- a/tests/neg/kind-projector.scala +++ b/tests/neg/kind-projector.scala @@ -1,4 +1,4 @@ -//> using options -Ykind-projector +//> using options -Ykind-projector:enable package kind_projector_neg diff --git a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala index b3250eb7b536..558eb3e0a12b 100644 --- a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala +++ b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala @@ -289,7 +289,7 @@ private sealed trait YSettings: val YstopBefore: Setting[List[String]] = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") val YdetailedStats: Setting[Boolean] = BooleanSetting("-Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") - val YkindProjector: Setting[String] = ChoiceSetting("-Ykind-projector", "[underscores, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") + val YkindProjector: Setting[String] = ChoiceSetting("-Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "enable", "underscores"), "disable") val YprintPos: Setting[Boolean] = BooleanSetting("-Yprint-pos", "Show tree positions.") val YprintPosSyms: Setting[Boolean] = BooleanSetting("-Yprint-pos-syms", "Show symbol definitions positions.") val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting("-Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") diff --git a/tests/pos/kind-projector.scala b/tests/pos/kind-projector.scala index 4d6ec8c932a9..07e6272efd90 100644 --- a/tests/pos/kind-projector.scala +++ b/tests/pos/kind-projector.scala @@ -1,4 +1,4 @@ -//> using options -Ykind-projector +//> using options -Ykind-projector:enable package kind_projector From bf0da9c7ee9c2d5888743878fef85aa1c19baa44 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 5 Mar 2024 18:12:22 +0100 Subject: [PATCH 10/15] Fix negative numeric args --- compiler/src/dotty/tools/dotc/config/Settings.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 7c687eb5988b..e998270a6052 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -187,9 +187,9 @@ object Settings: setBoolean(argRest, args) case (OptionTag, _) => update(Some(propertyClass.get.getConstructor().newInstance()), args) - case (_, args) => + case (ct, args) => val argInArgRest = !argRest.isEmpty - val argAfterParam = !argInArgRest && args.nonEmpty && !args.head.startsWith("-") + val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-")) if argInArgRest then doSetArg(argRest, args) else if argAfterParam then From d6d796dc84fc09fccaebe7f7dc7de29c0a0f76f0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 6 Mar 2024 16:42:45 +0100 Subject: [PATCH 11/15] Handle -Ykind-projector as legacy option --- .../src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- compiler/src/dotty/tools/dotc/config/Settings.scala | 10 ++++++++-- .../test/dotty/tools/dotc/TastyBootstrapTests.scala | 2 +- tests/neg/kind-projector.scala | 2 +- tests/pos/kind-projector.scala | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 835f5d5a6668..7eda918638b9 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -401,7 +401,7 @@ private sealed trait YSettings: val YstopBefore: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") val YdetailedStats: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") - val YkindProjector: Setting[String] = ChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "enable", "underscores"), "disable") + val YkindProjector: Setting[String] = LegacyChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") val YprintPos: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos", "Show tree positions.") val YprintPosSyms: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos-syms", "Show symbol definitions positions.") val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index e998270a6052..c64abb3dddb8 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -71,7 +71,9 @@ object Settings: depends: List[(Setting[?], Any)] = Nil, ignoreInvalidArgs: Boolean = false, propertyClass: Option[Class[?]] = None, - deprecationMsg: Option[String] = None)(private[Settings] val idx: Int) { + deprecationMsg: Option[String] = None, + // kept only for -Ykind-projector option compatibility + legacyArgs: Boolean = false)(private[Settings] val idx: Int) { assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") @@ -188,7 +190,7 @@ object Settings: case (OptionTag, _) => update(Some(propertyClass.get.getConstructor().newInstance()), args) case (ct, args) => - val argInArgRest = !argRest.isEmpty + val argInArgRest = !argRest.isEmpty || (legacyArgs && choices.exists(_.contains(""))) val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-")) if argInArgRest then doSetArg(argRest, args) @@ -336,6 +338,10 @@ object Settings: def ChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) + // Allows only args after :, but supports empty string as a choice. Used for -Ykind-projector + def LegacyChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting), legacyArgs = true)) + def MultiChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) diff --git a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala index 9929e1e87214..27311497de9c 100644 --- a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala +++ b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala @@ -51,7 +51,7 @@ class TastyBootstrapTests { Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm, Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader, ).mkString(File.pathSeparator), - Array("-Ycheck-reentrant", "-Ylog:checkReentrant+", "-language:postfixOps", "-Xsemanticdb") + Array("-Ycheck-reentrant", "-language:postfixOps", "-Xsemanticdb") ) val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped")) diff --git a/tests/neg/kind-projector.scala b/tests/neg/kind-projector.scala index fa2bd20d60ce..a7fc24c70b93 100644 --- a/tests/neg/kind-projector.scala +++ b/tests/neg/kind-projector.scala @@ -1,4 +1,4 @@ -//> using options -Ykind-projector:enable +//> using options -Ykind-projector package kind_projector_neg diff --git a/tests/pos/kind-projector.scala b/tests/pos/kind-projector.scala index 07e6272efd90..4d6ec8c932a9 100644 --- a/tests/pos/kind-projector.scala +++ b/tests/pos/kind-projector.scala @@ -1,4 +1,4 @@ -//> using options -Ykind-projector:enable +//> using options -Ykind-projector package kind_projector From 2f7ff98a65a702a371d36901942636f3b9e5d4a8 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 7 Mar 2024 14:06:40 +0100 Subject: [PATCH 12/15] Clean up the PR --- compiler/src/dotty/tools/backend/jvm/BackendUtils.scala | 2 +- compiler/src/dotty/tools/dotc/Driver.scala | 1 - compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 6 +++--- compiler/src/dotty/tools/dotc/config/Settings.scala | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index 459f1b2b2613..c953358d7940 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -186,4 +186,4 @@ object BackendUtils { "21" -> asm.Opcodes.V21, "22" -> asm.Opcodes.V22, ) -} \ No newline at end of file +} diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 6b461cc8e81b..196752aceb29 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -10,7 +10,6 @@ import dotty.tools.io.AbstractFile import reporting.* import core.Decorators.* import config.Feature -import dotty.tools.dotc.config.ScalaSettings import scala.util.control.NonFatal import fromtasty.{TASTYCompiler, TastyFileUtil} diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 7eda918638b9..07529be36c52 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -299,8 +299,8 @@ private sealed trait WarningSettings: val Wshadow: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "Wshadow", - helpArg = "advanced warning", - descr = "Enable or disable specific `lint` warnings", + 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"), @@ -359,7 +359,7 @@ private sealed trait XSettings: val XmacroSettings: Setting[List[String]] = MultiStringSetting(AdvancedSetting, "Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") // Deprecated - val Xlint: Setting[_] = DeprecatedSetting(AdvancedSetting, "Xlint", "Enable or disable specific warnings", "Use -Wshadow to enable shadowing lints or -W: to enable specific sets of warnings.") + val Xlint: Setting[_] = DeprecatedSetting(AdvancedSetting, "Xlint", "Enable or disable specific warnings", "Use -Wshadow to enable shadowing lints.") end XSettings diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index c64abb3dddb8..2fa484ba2882 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -77,7 +77,7 @@ object Settings: assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") - assert(!choices.contains(""), s"Empty string is not supported as a choice for setting $name") + 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") From 2ee562e0681c5361f1f2b9c5f7803cf4f20fefba Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 12 Mar 2024 12:30:32 +0100 Subject: [PATCH 13/15] Keep ScalaSettings class for back compat --- .../tools/dotc/config/CompilerCommand.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 54 ++++++++++--------- .../src/dotty/tools/dotc/core/Contexts.scala | 4 +- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 2e82c6a3cd67..587f94dad7b3 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -5,7 +5,7 @@ import Settings.* import core.Contexts.* abstract class CompilerCommand extends CliCommand: - type ConcreteSettings = ScalaSettings.type + type ConcreteSettings = ScalaSettings final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 07529be36c52..c27ba0863ebe 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -23,18 +23,7 @@ object ScalaSettingCategories: val AdvancedSetting = "X" val VerboseSetting = "V" -object ScalaSettings extends SettingGroup, AllScalaSettings: - val settingsByCategory: Map[String, List[Setting[_]]] = - allSettings.groupBy(_.category) - .view.mapValues(_.toList).toMap - .withDefaultValue(Nil) - def categories: List[String] = settingsByCategory.keys.toList - val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) - val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) - val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) - val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) - val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) - val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap +object ScalaSettingsProperties: private lazy val minTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).min private lazy val maxTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).max @@ -68,6 +57,23 @@ object ScalaSettings extends SettingGroup, AllScalaSettings: else defaultWidth else defaultWidth } + +object ScalaSettings extends ScalaSettings + +// Kept as seperate type to avoid breaking backward compatibility +abstract class ScalaSettings extends SettingGroup, AllScalaSettings: + val settingsByCategory: Map[String, List[Setting[_]]] = + allSettings.groupBy(_.category) + .view.mapValues(_.toList).toMap + .withDefaultValue(Nil) + def categories: List[String] = settingsByCategory.keys.toList + val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) + val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) + val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) + val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) + val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) + val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap + trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSettings, WarningSettings, XSettings, YSettings: self: SettingGroup => @@ -76,7 +82,7 @@ trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSetti val semanticdbTarget: Setting[String] = PathSetting(RootSetting, "semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "") val semanticdbText: Setting[Boolean] = BooleanSetting(RootSetting, "semanticdb-text", "Specifies whether to include source code in SemanticDB files or not.") - val source: Setting[String] = ChoiceSetting(RootSetting, "source", "source version", "source version", ScalaSettings.supportedSourceVersions, SourceVersion.defaultSourceVersion.toString, aliases = List("--source")) + val source: Setting[String] = ChoiceSetting(RootSetting, "source", "source version", "source version", ScalaSettingsProperties.supportedSourceVersions, SourceVersion.defaultSourceVersion.toString, aliases = List("--source")) val uniqid: Setting[Boolean] = BooleanSetting(RootSetting, "uniqid", "Uniquely tag all identifiers in debugging output.", aliases = List("--unique-id")) val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites](RootSetting, "rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.", aliases = List("--rewrite")) val fromTasty: Setting[Boolean] = BooleanSetting(RootSetting, "from-tasty", "Compile classes from tasty files. The arguments are .tasty or .jar files.", aliases = List("--from-tasty")) @@ -119,17 +125,17 @@ trait CommonScalaSettings: val sourcepath: Setting[String] = PathSetting(RootSetting, "sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath, aliases = List("--source-path")) val sourceroot: Setting[String] = PathSetting(RootSetting, "sourceroot", "Specify workspace root directory.", ".") - val classpath: Setting[String] = PathSetting(RootSetting, "classpath", "Specify where to find user class files.", ScalaSettings.defaultClasspath, aliases = List("-cp", "--class-path")) + val classpath: Setting[String] = PathSetting(RootSetting, "classpath", "Specify where to find user class files.", ScalaSettingsProperties.defaultClasspath, aliases = List("-cp", "--class-path")) val outputDir: Setting[AbstractFile] = OutputSetting(RootSetting, "d", "directory|jar", "Destination for generated classfiles.", new PlainDirectory(Directory("."))) val color: Setting[String] = ChoiceSetting(RootSetting, "color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/, aliases = List("--color")) val verbose: Setting[Boolean] = BooleanSetting(RootSetting, "verbose", "Output messages about what the compiler is doing.", aliases = List("--verbose")) val version: Setting[Boolean] = BooleanSetting(RootSetting, "version", "Print product version and exit.", aliases = List("--version")) val help: Setting[Boolean] = BooleanSetting(RootSetting, "help", "Print a synopsis of standard options.", aliases = List("--help", "-h")) - val pageWidth: Setting[Int] = IntSetting(RootSetting, "pagewidth", "Set page width", ScalaSettings.defaultPageWidth, aliases = List("--page-width")) + val pageWidth: Setting[Int] = IntSetting(RootSetting, "pagewidth", "Set page width", ScalaSettingsProperties.defaultPageWidth, aliases = List("--page-width")) val silentWarnings: Setting[Boolean] = BooleanSetting(RootSetting, "nowarn", "Silence all warnings.", aliases = List("--no-warnings")) - val javaOutputVersion: Setting[String] = ChoiceSetting(RootSetting, "java-output-version", "version", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version. Corresponds to -release flag in javac.", ScalaSettings.supportedReleaseVersions, "", aliases = List("-release", "--release")) + val javaOutputVersion: Setting[String] = ChoiceSetting(RootSetting, "java-output-version", "version", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version. Corresponds to -release flag in javac.", ScalaSettingsProperties.supportedReleaseVersions, "", aliases = List("-release", "--release")) val deprecation: Setting[Boolean] = BooleanSetting(RootSetting, "deprecation", "Emit warning and location for usages of deprecated APIs.", aliases = List("--deprecation")) val feature: Setting[Boolean] = BooleanSetting(RootSetting, "feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature")) @@ -181,13 +187,13 @@ private sealed trait VerboseSettings: private sealed trait WarningSettings: self: SettingGroup => - val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "-W", "Print a synopsis of warning options.") - val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) - val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "-Wvalue-discard", "Warn when non-Unit expression results are unused.") - val WNonUnitStatement = BooleanSetting(WarningSetting, "-Wnonunit-statement", "Warn when block statements are non-Unit expressions.") - 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.") - val WunstableInlineAccessors = BooleanSetting(WarningSetting, "-WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") + val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "W", "Print a synopsis of warning options.") + val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) + val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "Wvalue-discard", "Warn when non-Unit expression results are unused.") + val WNonUnitStatement = BooleanSetting(WarningSetting, "Wnonunit-statement", "Warn when block statements are non-Unit expressions.") + 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.") + val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "Wunused", @@ -338,7 +344,7 @@ private sealed trait XSettings: val XignoreScala2Macros: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.") val XimportSuggestionTimeout: Setting[Int] = IntSetting(AdvancedSetting, "Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) val Xsemanticdb: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xsemanticdb", "Store information in SemanticDB.", aliases = List("-Ysemanticdb")) - val XuncheckedJavaOutputVersion: Setting[String] = ChoiceSetting(AdvancedSetting, "Xunchecked-java-output-version", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. Corresponds to -target flag in javac. When on JDK 9+, consider -java-output-version as a safer alternative.", ScalaSettings.supportedTargetVersions, "", aliases = List("-Xtarget", "--Xtarget")) + val XuncheckedJavaOutputVersion: Setting[String] = ChoiceSetting(AdvancedSetting, "Xunchecked-java-output-version", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. Corresponds to -target flag in javac. When on JDK 9+, consider -java-output-version as a safer alternative.", ScalaSettingsProperties.supportedTargetVersions, "", aliases = List("-Xtarget", "--Xtarget")) val XcheckMacros: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros")) val XmainClass: Setting[String] = StringSetting(AdvancedSetting, "Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d )", "") val XimplicitSearchLimit: Setting[Int] = IntSetting(AdvancedSetting, "Ximplicit-search-limit", "Maximal number of expressions to be generated in an implicit search", 50000) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b94824ac3a2b..ae21c6fb8763 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -513,7 +513,7 @@ object Contexts { s"""Context( |${outersIterator.map(ctx => cinfo(using ctx)).mkString("\n\n")})""".stripMargin - def settings: ScalaSettings.type = base.settings + def settings: ScalaSettings = base.settings def definitions: Definitions = base.definitions def platform: Platform = base.platform def pendingUnderlying: util.HashSet[Type] = base.pendingUnderlying @@ -861,7 +861,7 @@ object Contexts { with Phases.PhasesBase with Plugins { - val settings: ScalaSettings.type = ScalaSettings + val settings: ScalaSettings = ScalaSettings /** The initial context */ val initialCtx: Context = FreshContext.initial(this: @unchecked, settings) From 2b3c05aa0e35127e237bad6f7b156f08c447d933 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 13 Mar 2024 17:13:44 +0100 Subject: [PATCH 14/15] Applied review suggestions --- .../tools/backend/jvm/BackendUtils.scala | 34 +++---- .../tools/dotc/config/ScalaSettings.scala | 69 ++++---------- .../dotc/config/ScalaSettingsProperties.scala | 41 +++++++++ .../dotty/tools/dotc/config/Settings.scala | 90 ++++++++++--------- .../test/dotty/tools/dotc/SettingsTests.scala | 31 +++---- .../dotc/config/ScalaSettingsTests.scala | 10 +-- 6 files changed, 145 insertions(+), 130 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index c953358d7940..865ee9bf4af9 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -20,7 +20,7 @@ class BackendUtils(val postProcessor: PostProcessor) { import bTypes.* import coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle - lazy val classfileVersion: Int = BackendUtils.classfileVersionMap(compilerSettings.target) + lazy val classfileVersion: Int = BackendUtils.classfileVersionMap(compilerSettings.target.toInt) lazy val extraProc: Int = { import GenBCodeOps.addFlagIf @@ -169,21 +169,21 @@ class BackendUtils(val postProcessor: PostProcessor) { } object BackendUtils { - lazy val classfileVersionMap = Map( - "8" -> asm.Opcodes.V1_8, - "9" -> asm.Opcodes.V9, - "10" -> asm.Opcodes.V10, - "11" -> asm.Opcodes.V11, - "12" -> asm.Opcodes.V12, - "13" -> asm.Opcodes.V13, - "14" -> asm.Opcodes.V14, - "15" -> asm.Opcodes.V15, - "16" -> asm.Opcodes.V16, - "17" -> asm.Opcodes.V17, - "18" -> asm.Opcodes.V18, - "19" -> asm.Opcodes.V19, - "20" -> asm.Opcodes.V20, - "21" -> asm.Opcodes.V21, - "22" -> asm.Opcodes.V22, + lazy val classfileVersionMap: Map[Int, Int] = Map( + 8 -> asm.Opcodes.V1_8, + 9 -> asm.Opcodes.V9, + 10 -> asm.Opcodes.V10, + 11 -> asm.Opcodes.V11, + 12 -> asm.Opcodes.V12, + 13 -> asm.Opcodes.V13, + 14 -> asm.Opcodes.V14, + 15 -> asm.Opcodes.V15, + 16 -> asm.Opcodes.V16, + 17 -> asm.Opcodes.V17, + 18 -> asm.Opcodes.V18, + 19 -> asm.Opcodes.V19, + 20 -> asm.Opcodes.V20, + 21 -> asm.Opcodes.V21, + 22 -> asm.Opcodes.V22, ) } diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index c27ba0863ebe..12fe7df8d4d1 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -2,9 +2,8 @@ package dotty.tools.dotc package config import scala.language.unsafeNulls -import dotty.tools.backend.jvm.BackendUtils.classfileVersionMap import dotty.tools.dotc.config.PathResolver.Defaults -import dotty.tools.dotc.config.Settings.{Setting, SettingGroup} +import dotty.tools.dotc.config.Settings.{Setting, SettingGroup, SettingCategory} import dotty.tools.dotc.config.SourceVersion import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.rewrites.Rewrites @@ -16,62 +15,32 @@ import scala.util.chaining.* import java.util.zip.Deflater -object ScalaSettingCategories: - val RootSetting = "" - val WarningSetting = "W" - val ForkSetting = "Y" - val AdvancedSetting = "X" - val VerboseSetting = "V" - -object ScalaSettingsProperties: - - private lazy val minTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).min - private lazy val maxTargetVersion = classfileVersionMap.keysIterator.map(_.toInt).max - - def supportedTargetVersions: List[String] = - (minTargetVersion to maxTargetVersion).toList.map(_.toString) - - def supportedReleaseVersions: List[String] = - if scala.util.Properties.isJavaAtLeast("9") then - val jdkVersion = JDK9Reflectors.runtimeVersionMajor(JDK9Reflectors.runtimeVersion()).intValue() - val maxVersion = Math.min(jdkVersion, maxTargetVersion) - (minTargetVersion to maxVersion).toList.map(_.toString) - else List(minTargetVersion).map(_.toString) - - def supportedScalaReleaseVersions: List[String] = - ScalaRelease.values.toList.map(_.show) - - def supportedSourceVersions: List[String] = - SourceVersion.values.toList.map(_.toString) - - def defaultClasspath: String = sys.env.getOrElse("CLASSPATH", ".") - - def defaultPageWidth: Int = { - val defaultWidth = 80 - val columnsVar = System.getenv("COLUMNS") - if columnsVar != null then columnsVar.toInt - else if Properties.isWin then - val ansiconVar = System.getenv("ANSICON") // eg. "142x32766 (142x26)" - if ansiconVar != null && ansiconVar.matches("[0-9]+x.*") then - ansiconVar.substring(0, ansiconVar.indexOf("x")).toInt - else defaultWidth - else defaultWidth - } +enum ScalaSettingCategories(val prefixLetter: String) extends SettingCategory: + // Root settings, a category for setting that are used to configure the core compilation process + case RootSetting extends ScalaSettingCategories("") + // Warning settings, a category for settings that are used to enable and configure warnings + case WarningSetting extends ScalaSettingCategories("W") + // Fork / private settings, a category for settings that enable private or advanced features, mainly used for debugging the compiler + case ForkSetting extends ScalaSettingCategories("Y") + // Advanced settings, a category for settings that enable advanced, often unstable, features + case AdvancedSetting extends ScalaSettingCategories("X") + // Verbose settings, a category to configure the verbosity of the compiler + case VerboseSetting extends ScalaSettingCategories("V") object ScalaSettings extends ScalaSettings // Kept as seperate type to avoid breaking backward compatibility abstract class ScalaSettings extends SettingGroup, AllScalaSettings: - val settingsByCategory: Map[String, List[Setting[_]]] = + val settingsByCategory: Map[SettingCategory, List[Setting[_]]] = allSettings.groupBy(_.category) .view.mapValues(_.toList).toMap .withDefaultValue(Nil) - def categories: List[String] = settingsByCategory.keys.toList - val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting) - val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting) - val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting) - val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting) - val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting) + def categories: List[SettingCategory] = settingsByCategory.keys.toList.sortBy(_.prefixLetter) + val rootSettings: List[Setting[_]] = settingsByCategory(RootSetting).sortBy(_.name) + val warningSettings: List[Setting[_]] = settingsByCategory(WarningSetting).sortBy(_.name) + val forkSettings: List[Setting[_]] = settingsByCategory(ForkSetting).sortBy(_.name) + val advancedSettings: List[Setting[_]] = settingsByCategory(AdvancedSetting).sortBy(_.name) + val verboseSettings: List[Setting[_]] = settingsByCategory(VerboseSetting).sortBy(_.name) val settingsByAliases: Map[String, Setting[_]] = allSettings.flatMap(s => s.aliases.map(_ -> s)).toMap diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala new file mode 100644 index 000000000000..e8a55dc6e737 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala @@ -0,0 +1,41 @@ +package dotty.tools.dotc +package config + +import dotty.tools.backend.jvm.BackendUtils.classfileVersionMap +import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory, NoAbstractFile} +import scala.language.unsafeNulls + +object ScalaSettingsProperties: + + private lazy val minTargetVersion = classfileVersionMap.keysIterator.min + private lazy val maxTargetVersion = classfileVersionMap.keysIterator.max + + def supportedTargetVersions: List[String] = + (minTargetVersion to maxTargetVersion).toList.map(_.toString) + + def supportedReleaseVersions: List[String] = + if scala.util.Properties.isJavaAtLeast("9") then + val jdkVersion = JDK9Reflectors.runtimeVersionMajor(JDK9Reflectors.runtimeVersion()).intValue() + val maxVersion = Math.min(jdkVersion, maxTargetVersion) + (minTargetVersion to maxVersion).toList.map(_.toString) + else List(minTargetVersion).map(_.toString) + + def supportedScalaReleaseVersions: List[String] = + ScalaRelease.values.toList.map(_.show) + + def supportedSourceVersions: List[String] = + SourceVersion.values.toList.map(_.toString) + + def defaultClasspath: String = sys.env.getOrElse("CLASSPATH", ".") + + def defaultPageWidth: Int = { + val defaultWidth = 80 + val columnsVar = System.getenv("COLUMNS") + if columnsVar != null then columnsVar.toInt + else if Properties.isWin then + val ansiconVar = System.getenv("ANSICON") // eg. "142x32766 (142x26)" + if ansiconVar != null && ansiconVar.matches("[0-9]+x.*") then + ansiconVar.substring(0, ansiconVar.indexOf("x")).toInt + else defaultWidth + else defaultWidth + } diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 2fa484ba2882..649fd600bdc2 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -25,6 +25,9 @@ object Settings: val OptionTag: ClassTag[Option[?]] = ClassTag(classOf[Option[?]]) val OutputTag: ClassTag[AbstractFile] = ClassTag(classOf[AbstractFile]) + trait SettingCategory: + def prefixLetter: String + class SettingsState(initialValues: Seq[Any], initialChanged: Set[Int] = Set.empty): private val values = ArrayBuffer(initialValues*) private val changed: mutable.Set[Int] = initialChanged.to(mutable.Set) @@ -59,8 +62,14 @@ object Settings: ArgsSummary(sstate, arguments.tail, errors, warnings :+ msg) } + @unshared + val settingCharacters = "[a-zA-Z0-9_\\-]*".r + def validateSettingString(name: String): Unit = + assert(settingCharacters.matches(name), s"Setting string $name contains invalid characters") + + case class Setting[T: ClassTag] private[Settings] ( - category: String, + category: SettingCategory, name: String, description: String, default: T, @@ -75,8 +84,9 @@ object Settings: // kept only for -Ykind-projector option compatibility legacyArgs: Boolean = false)(private[Settings] val idx: Int) { - - assert(name.startsWith(s"-$category"), s"Setting $name does not start with category -$category") + 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. @@ -319,64 +329,58 @@ object Settings: setting } - @unshared - val settingCharacters = "[a-zA-Z0-9_\\-]*".r - def validateSetting(setting: String): String = - assert(settingCharacters.matches(setting), s"Setting $setting contains invalid characters") - setting - - def validateAndPrependName(name: String): String = + def prependName(name: String): String = assert(!name.startsWith("-"), s"Setting $name cannot start with -") - "-" + validateSetting(name) + "-" + name - def BooleanSetting(category: String, name: String, descr: String, initialValue: Boolean = false, aliases: List[String] = Nil): Setting[Boolean] = - publish(Setting(category, validateAndPrependName(name), descr, initialValue, aliases = aliases.map(validateSetting))) + def BooleanSetting(category: SettingCategory, name: String, descr: String, initialValue: Boolean = false, aliases: List[String] = Nil): Setting[Boolean] = + publish(Setting(category, prependName(name), descr, initialValue, aliases = aliases)) - def StringSetting(category: String, name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, aliases = aliases.map(validateSetting))) + def StringSetting(category: SettingCategory, name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, prependName(name), descr, default, helpArg, aliases = aliases)) - def ChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) + def ChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases)) // Allows only args after :, but supports empty string as a choice. Used for -Ykind-projector - def LegacyChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting), legacyArgs = true)) + def LegacyChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, legacyArgs = true)) - def MultiChoiceSetting(category: String, name: String, helpArg: String, descr: String, choices: List[String], default: List[String], aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) + 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: String, 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, validateAndPrependName(name), descr, default, helpArg, Some(choices), aliases = aliases.map(validateSetting))) + 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 IntSetting(category: String, name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = - publish(Setting(category, validateAndPrependName(name), descr, default, aliases = aliases.map(validateSetting))) + 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)) - def IntChoiceSetting(category: String, name: String, descr: String, choices: Seq[Int], default: Int): Setting[Int] = - publish(Setting(category, validateAndPrependName(name), descr, default, choices = Some(choices))) + def IntChoiceSetting(category: SettingCategory, name: String, descr: String, choices: Seq[Int], default: Int): Setting[Int] = + publish(Setting(category, prependName(name), descr, default, choices = Some(choices))) - def MultiStringSetting(category: String, name: String, helpArg: String, descr: String, default: List[String] = Nil, aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg, aliases = aliases.map(validateSetting))) + def MultiStringSetting(category: SettingCategory, name: String, helpArg: String, descr: String, default: List[String] = Nil, aliases: List[String] = Nil): Setting[List[String]] = + publish(Setting(category, prependName(name), descr, default, helpArg, aliases = aliases)) - def OutputSetting(category: String, name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] = - publish(Setting(category, validateAndPrependName(name), descr, default, helpArg)) + def OutputSetting(category: SettingCategory, name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] = + publish(Setting(category, prependName(name), descr, default, helpArg)) - def PathSetting(category: String, name: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, validateAndPrependName(name), descr, default, aliases = aliases.map(validateSetting))) + def PathSetting(category: SettingCategory, name: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(category, prependName(name), descr, default, aliases = aliases)) - def PhasesSetting(category: String, name: String, descr: String, default: String = "", aliases: List[String] = Nil): Setting[List[String]] = - publish(Setting(category, validateAndPrependName(name), descr, if (default.isEmpty) Nil else List(default), aliases = aliases.map(validateSetting))) + def PhasesSetting(category: SettingCategory, name: String, descr: String, default: String = "", aliases: List[String] = Nil): Setting[List[String]] = + publish(Setting(category, prependName(name), descr, if (default.isEmpty) Nil else List(default), aliases = aliases)) - def PrefixSetting(category: String, name: String, descr: String): Setting[List[String]] = + def PrefixSetting(category: SettingCategory, name: String, descr: String): Setting[List[String]] = val prefix = name.takeWhile(_ != '<') - publish(Setting(category, "-" + name, descr, Nil, prefix = Some(validateSetting(prefix)))) + publish(Setting(category, "-" + name, descr, Nil, prefix = Some(prefix))) - def VersionSetting(category: String, name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = - publish(Setting(category, validateAndPrependName(name), descr, default)) + def VersionSetting(category: SettingCategory, name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = + publish(Setting(category, prependName(name), descr, default)) - def OptionSetting[T: ClassTag](category: String, name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = - publish(Setting(category, validateAndPrependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases.map(validateSetting))) + def OptionSetting[T: ClassTag](category: SettingCategory, name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = + publish(Setting(category, prependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases)) - def DeprecatedSetting(category: String, name: String, descr: String, deprecationMsg: String): Setting[Boolean] = - publish(Setting(category, validateAndPrependName(name), descr, false, deprecationMsg = Some(deprecationMsg))) + def DeprecatedSetting(category: SettingCategory, name: String, descr: String, deprecationMsg: String): Setting[Boolean] = + publish(Setting(category, prependName(name), descr, false, deprecationMsg = Some(deprecationMsg))) } end Settings diff --git a/compiler/test/dotty/tools/dotc/SettingsTests.scala b/compiler/test/dotty/tools/dotc/SettingsTests.scala index 595c4875bb12..8125a80f29f8 100644 --- a/compiler/test/dotty/tools/dotc/SettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/SettingsTests.scala @@ -8,6 +8,7 @@ import vulpix.TestConfiguration import core.Contexts.{Context, ContextBase} import dotty.tools.dotc.config.Settings._ +import dotty.tools.dotc.config.ScalaSettingCategories._ import dotty.tools.vulpix.TestConfiguration.mkClasspath import java.nio.file._ @@ -43,8 +44,8 @@ class SettingsTests { @Test def acceptUnconstrained: Unit = object Settings extends SettingGroup: - val foo = StringSetting("", "foo", "foo", "Foo", "a") - val bar = IntSetting("", "bar", "Bar", 0) + val foo = StringSetting(RootSetting, "foo", "foo", "Foo", "a") + val bar = IntSetting(RootSetting, "bar", "Bar", 0) val args = List("-foo", "b", "-bar", "1") val summary = Settings.processArguments(args, true) @@ -72,7 +73,7 @@ class SettingsTests { @Test def `dont crash on many options`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "option", "Some option") + val option = BooleanSetting(RootSetting, "option", "Some option") val limit = 6000 val args = List.fill(limit)("-option") @@ -87,7 +88,7 @@ class SettingsTests { @Test def `bad option warning consumes an arg`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "option", "Some option") + val option = BooleanSetting(RootSetting, "option", "Some option") val args = List("-adoption", "dogs", "cats") val summary = Settings.processArguments(args, processAll = true) @@ -97,7 +98,7 @@ class SettingsTests { @Test def `bad option settings throws`: Unit = object Settings extends SettingGroup: - val option = BooleanSetting("", "option", "Some option") + val option = BooleanSetting(RootSetting, "option", "Some option") def checkMessage(s: String): (Throwable => Boolean) = t => if t.getMessage == s then true @@ -112,12 +113,12 @@ class SettingsTests { @Test def validateChoices: Unit = object Settings extends SettingGroup: - val foo = ChoiceSetting("", "foo", "foo", "Foo", List("a", "b"), "a") - val bar = IntChoiceSetting("", "bar", "Bar", List(0, 1, 2), 0) - val baz = IntChoiceSetting("", "baz", "Baz", 0 to 10, 10) + val foo = ChoiceSetting(RootSetting, "foo", "foo", "Foo", List("a", "b"), "a") + val bar = IntChoiceSetting(RootSetting, "bar", "Bar", List(0, 1, 2), 0) + val baz = IntChoiceSetting(RootSetting, "baz", "Baz", 0 to 10, 10) - val quux = ChoiceSetting("", "quux", "quux", "Quux", List(), "") - val quuz = IntChoiceSetting("", "quuz", "Quuz", List(), 0) + val quux = ChoiceSetting(RootSetting, "quux", "quux", "Quux", List(), "") + val quuz = IntChoiceSetting(RootSetting, "quuz", "Quuz", List(), 0) locally { val args = List("-foo", "b", "-bar", "1", "-baz", "5") @@ -169,7 +170,7 @@ class SettingsTests { @Test def `Allow IntSetting's to be set with a colon`: Unit = object Settings extends SettingGroup: - val foo = IntSetting("", "foo", "foo", 80) + val foo = IntSetting(RootSetting, "foo", "foo", 80) import Settings._ val args = List("-foo:100") @@ -181,10 +182,10 @@ class SettingsTests { @Test def `Set BooleanSettings correctly`: Unit = object Settings extends SettingGroup: - val foo = BooleanSetting("", "foo", "foo", false) - val bar = BooleanSetting("", "bar", "bar", true) - val baz = BooleanSetting("", "baz", "baz", false) - val qux = BooleanSetting("", "qux", "qux", false) + val foo = BooleanSetting(RootSetting, "foo", "foo", false) + val bar = BooleanSetting(RootSetting, "bar", "bar", true) + val baz = BooleanSetting(RootSetting, "baz", "baz", false) + val qux = BooleanSetting(RootSetting, "qux", "qux", false) import Settings._ val args = List("-foo:true", "-bar:false", "-baz", "-qux:true", "-qux:false") diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 77691e1008e0..e958a5925fce 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -3,7 +3,7 @@ package config import CommandLineParser.tokenize import Settings._ - +import dotty.tools.dotc.config.ScalaSettingCategories._ import org.junit.Test import org.junit.Assert._ import core.Decorators.toMessage @@ -12,7 +12,7 @@ class ScalaSettingsTests: @Test def `A setting with aliases is accepted`: Unit = class MySettings extends SettingGroup: - val classpath: Setting[String] = PathSetting("", "classpath", "Specify where to find user class files.", ".", aliases = List("--class-path", "-cp")) + val classpath: Setting[String] = PathSetting(RootSetting, "classpath", "Specify where to find user class files.", ".", aliases = List("--class-path", "-cp")) val settings = MySettings() val args = tokenize("-cp path/to/classes1:other/path/to/classes2") val summary = ArgsSummary(settings.defaultState, args, errors = Nil, warnings = Nil) @@ -25,7 +25,7 @@ class ScalaSettingsTests: @Test def `A multistring setting is multivalued`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting(RootSetting, "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:implicitConversions,dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) @@ -39,7 +39,7 @@ class ScalaSettingsTests: @Test def `t9719 Apply -language more than once`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting(RootSetting, "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:implicitConversions -language:dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) @@ -53,7 +53,7 @@ class ScalaSettingsTests: @Test def `Warn if multistring element is supplied multiply`: Unit = class SUT extends SettingGroup: - val language: Setting[List[String]] = MultiStringSetting("", "language", "feature", "Enable one or more language features.") + val language: Setting[List[String]] = MultiStringSetting(RootSetting, "language", "feature", "Enable one or more language features.") val sut = SUT() val args = tokenize("-language:dynamics -language:implicitConversions -language:dynamics") val sumy = ArgsSummary(sut.defaultState, args, errors = Nil, warnings = Nil) From 9a166171b00d0f81c879524fc185a31667c60022 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 18 Mar 2024 13:31:33 +0100 Subject: [PATCH 15/15] Fix -Xmigration legacy behavior --- .../dotty/tools/dotc/config/ScalaSettings.scala | 4 ++-- .../src/dotty/tools/dotc/config/Settings.scala | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 12fe7df8d4d1..687adfe05ca7 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -301,7 +301,7 @@ private sealed trait XSettings: val XnoForwarders: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xno-forwarders", "Do not generate static forwarders in mirror classes.") val XmaxInlines: Setting[Int] = IntSetting(AdvancedSetting, "Xmax-inlines", "Maximal number of successive inlines.", 32) val XmaxInlinedTrees: Setting[Int] = IntSetting(AdvancedSetting, "Xmax-inlined-trees", "Maximal number of inlined trees.", 2_000_000) - val Xmigration: Setting[ScalaVersion] = VersionSetting(AdvancedSetting, "Xmigration", "Warn about constructs whose behavior may have changed since version.") + val Xmigration: Setting[ScalaVersion] = VersionSetting(AdvancedSetting, "Xmigration", "Warn about constructs whose behavior may have changed since version.", legacyArgs = true) val XprintTypes: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-types", "Print tree types (debugging option).") val XprintDiff: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-diff", "Print changed parts of the tree since last print.") val XprintDiffDel: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xprint-diff-del", "Print changed parts of the tree since last print including deleted parts.") @@ -376,7 +376,7 @@ private sealed trait YSettings: val YstopBefore: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") val YdetailedStats: Setting[Boolean] = BooleanSetting(ForkSetting, "Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") - val YkindProjector: Setting[String] = LegacyChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") + val YkindProjector: Setting[String] = ChoiceSetting(ForkSetting, "Ykind-projector", "[underscores, enable, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable", legacyArgs = true) val YprintPos: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos", "Show tree positions.") val YprintPosSyms: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprint-pos-syms", "Show symbol definitions positions.") val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.") diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 649fd600bdc2..a65072427ba7 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -200,7 +200,7 @@ object Settings: case (OptionTag, _) => update(Some(propertyClass.get.getConstructor().newInstance()), args) case (ct, args) => - val argInArgRest = !argRest.isEmpty || (legacyArgs && choices.exists(_.contains(""))) + val argInArgRest = !argRest.isEmpty || legacyArgs val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-")) if argInArgRest then doSetArg(argRest, args) @@ -339,12 +339,8 @@ object Settings: def StringSetting(category: SettingCategory, name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = publish(Setting(category, prependName(name), descr, default, helpArg, aliases = aliases)) - def ChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases)) - - // Allows only args after :, but supports empty string as a choice. Used for -Ykind-projector - def LegacyChoiceSetting(category: SettingCategory, name: String, helpArg: String, descr: String, choices: List[String], default: String, aliases: List[String] = Nil): Setting[String] = - publish(Setting(category, prependName(name), descr, default, helpArg, Some(choices), aliases = aliases, legacyArgs = true)) + 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)) @@ -374,8 +370,8 @@ object Settings: val prefix = name.takeWhile(_ != '<') publish(Setting(category, "-" + name, descr, Nil, prefix = Some(prefix))) - def VersionSetting(category: SettingCategory, name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = - publish(Setting(category, prependName(name), descr, default)) + def VersionSetting(category: SettingCategory, name: String, descr: String, default: ScalaVersion = NoScalaVersion, legacyArgs: Boolean = false): Setting[ScalaVersion] = + publish(Setting(category, prependName(name), descr, default, legacyArgs = legacyArgs)) def OptionSetting[T: ClassTag](category: SettingCategory, name: String, descr: String, aliases: List[String] = Nil): Setting[Option[T]] = publish(Setting(category, prependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases))