Skip to content

Commit dffc44c

Browse files
committed
-Vphases takes names to mark, -Vprint is typer
Distinguish whether a setting was "set" by the user. -Vphases takes names to append an asterisk for easy scanning, and defaults to "none" to mark; -Vprint defaults to "typer". Both options are used if the user used them: setting.isPresent. If the setting value is not supplied, use a nonempty default. An "empty" default, such as an empty string or list, would require an explicit arg from the user.
1 parent 4bd3369 commit dffc44c

File tree

5 files changed

+46
-26
lines changed

5 files changed

+46
-26
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
343343
try units = phase.runOn(units)
344344
catch case _: InterruptedException => cancelInterrupted()
345345
profiler.afterPhase(phase, profileBefore)
346-
if (ctx.settings.Xprint.value.containsPhase(phase))
346+
for (printAt <- ctx.settings.Xprint.userValue if printAt.containsPhase(phase))
347347
for (unit <- units)
348348
def printCtx(unit: CompilationUnit) = phase.printingContext(
349349
ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ trait CliCommand:
160160
val maxCol = ctx.settings.pageWidth.value
161161
val field1 = maxField.min(texts.flatten.map(_._1.length).filter(_ < maxField).max) // widest field under maxField
162162
val field2 = if field1 + separation + maxField < maxCol then maxCol - field1 - separation else 0 // skinny window -> terminal wrap
163-
val separator = " " * separation
163+
val toMark = ctx.settings.XshowPhases.value.toSet
164+
def separator(name: String) = if toMark(name) then "*" + " " * (separation - 1) else " " * separation
164165
val EOL = "\n"
165166
def formatField1(text: String): String = if text.length <= field1 then text.padLeft(field1) else text + EOL + "".padLeft(field1)
166167
def formatField2(text: String): String =
@@ -170,11 +171,11 @@ trait CliCommand:
170171
fld.lastIndexOf(" ", field2) match
171172
case -1 => List(fld)
172173
case i => val (prefix, rest) = fld.splitAt(i) ; prefix :: loopOverField2(rest.trim)
173-
text.split("\n").toList.flatMap(loopOverField2).filter(_.nonEmpty).mkString(EOL + "".padLeft(field1) + separator)
174+
text.split("\n").toList.flatMap(loopOverField2).filter(_.nonEmpty).mkString(EOL + "".padLeft(field1) + separator("no-phase"))
174175
end formatField2
175176
def format(first: String, second: String, index: Int, colorPicker: Int => String => Highlight) =
176177
sb.append(colorPicker(index)(formatField1(first)).show)
177-
.append(separator)
178+
.append(colorPicker(index)(separator(first)).show)
178179
.append(formatField2(second))
179180
.append(EOL): Unit
180181
def fancy(first: String, second: String, index: Int) = format(first, second, index, color)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ abstract class CompilerCommand extends CliCommand:
1717
else if (settings.Xhelp.value) xusageMessage
1818
else if (settings.Yhelp.value) yusageMessage
1919
else if (settings.showPlugins.value) ctx.base.pluginDescriptions
20-
else if (settings.XshowPhases.value) phasesMessage
20+
else if settings.XshowPhases.isPresentIn(summon[SettingsState]) then phasesMessage
2121
else ""
2222

2323
final def isHelpFlag(using settings: ConcreteSettings)(using SettingsState): Boolean =
2424
import settings.*
25-
val flags = Set(help, Vhelp, Whelp, Xhelp, Yhelp, showPlugins, XshowPhases)
26-
flags.exists(_.value) || allSettings.exists(isHelping)
25+
val flags = Set(help, Vhelp, Whelp, Xhelp, Yhelp, showPlugins)
26+
flags.exists(_.value) || XshowPhases.isPresentIn(summon[SettingsState]) || allSettings.exists(isHelping)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ private sealed trait PluginSettings:
142142
private sealed trait VerboseSettings:
143143
self: SettingGroup =>
144144
val Vhelp: Setting[Boolean] = BooleanSetting(VerboseSetting, "V", "Print a synopsis of verbose options.")
145-
val Xprint: Setting[List[String]] = PhasesSetting(VerboseSetting, "Vprint", "Print out program after", aliases = List("-Xprint"))
146-
val XshowPhases: Setting[Boolean] = BooleanSetting(VerboseSetting, "Vphases", "List compiler phases.", aliases = List("-Xshow-phases"))
145+
val Xprint: Setting[List[String]] = PhasesSetting(VerboseSetting, "Vprint", "Print out program after", default = "typer", aliases = List("-Xprint"))
146+
val XshowPhases: Setting[List[String]] = PhasesSetting(VerboseSetting, "Vphases", "List compiler phases.", default = "none", aliases = List("-Xshow-phases"))
147147

148148
val Vprofile: Setting[Boolean] = BooleanSetting(VerboseSetting, "Vprofile", "Show metrics about sources and internal representations to estimate compile-time complexity.")
149149
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"), "")

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

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import dotty.tools.dotc.config.Settings.Setting.ChoiceWithHelp
1717

1818
object Settings:
1919

20+
private inline def classTag[T](using ctag: ClassTag[T]): ClassTag[T] = ctag
21+
2022
val BooleanTag: ClassTag[Boolean] = ClassTag.Boolean
2123
val IntTag: ClassTag[Int] = ClassTag.Int
2224
val StringTag: ClassTag[String] = ClassTag(classOf[String])
@@ -93,21 +95,25 @@ object Settings:
9395
assert(legacyArgs || !choices.exists(_.contains("")), s"Empty string is not supported as a choice for setting $name")
9496
// Without the following assertion, it would be easy to mistakenly try to pass a file to a setting that ignores invalid args.
9597
// Example: -opt Main.scala would be interpreted as -opt:Main.scala, and the source file would be ignored.
96-
assert(!(summon[ClassTag[T]] == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name")
98+
assert(!(classTag[T] == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name")
9799

98100
val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases
99101

102+
def isPresentIn(state: SettingsState): Boolean = state.wasChanged(idx)
103+
100104
def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T]
101105

106+
def userValueIn(state: SettingsState): Option[T] = if isPresentIn(state) then Some(valueIn(state)) else None
107+
102108
def updateIn(state: SettingsState, x: Any): SettingsState = x match
103109
case _: T => state.update(idx, x)
104-
case _ => throw IllegalArgumentException(s"found: $x of type ${x.getClass.getName}, required: ${summon[ClassTag[T]]}")
110+
case _ => throw IllegalArgumentException(s"found: $x of type ${x.getClass.getName}, required: ${classTag[T]}")
105111

106112
def isDefaultIn(state: SettingsState): Boolean = valueIn(state) == default
107113

108-
def isMultivalue: Boolean = summon[ClassTag[T]] == ListTag
114+
def isMultivalue: Boolean = classTag[T] == ListTag
109115

110-
def acceptsNoArg: Boolean = summon[ClassTag[T]] == BooleanTag || summon[ClassTag[T]] == OptionTag || choices.exists(_.contains(""))
116+
def acceptsNoArg: Boolean = classTag[T] == BooleanTag || classTag[T] == OptionTag || choices.exists(_.contains(""))
111117

112118
def legalChoices: String =
113119
choices match
@@ -123,7 +129,7 @@ object Settings:
123129
* Updates the value in state
124130
*
125131
* @param getValue it is crucial that this argument is passed by name, as [setOutput] have side effects.
126-
* @param argStringValue string value of currently proccessed argument that will be used to set deprecation replacement
132+
* @param argStringValue string value of currently processed argument that will be used to set deprecation replacement
127133
* @param args remaining arguments to process
128134
* @return new argumment state
129135
*/
@@ -159,11 +165,17 @@ object Settings:
159165

160166
def missingArg =
161167
val msg = s"missing argument for option $name"
162-
if ignoreInvalidArgs then state.warn(msg + ", the tag was ignored") else state.fail(msg)
168+
if ignoreInvalidArgs then state.warn(s"$msg, the tag was ignored") else state.fail(msg)
163169

164170
def invalidChoices(invalid: List[String]) =
165171
val msg = s"invalid choice(s) for $name: ${invalid.mkString(",")}"
166-
if ignoreInvalidArgs then state.warn(msg + ", the tag was ignored") else state.fail(msg)
172+
if ignoreInvalidArgs then state.warn(s"$msg, the tag was ignored") else state.fail(msg)
173+
174+
def isEmptyDefault = default == null.asInstanceOf[T] || classTag[T].match
175+
case ListTag => default.asInstanceOf[List[?]].isEmpty
176+
case StringTag => default.asInstanceOf[String].isEmpty
177+
case OptionTag => default.asInstanceOf[Option[?]].isEmpty
178+
case _ => false
167179

168180
def setBoolean(argValue: String, args: List[String]) =
169181
if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, argValue, args)
@@ -192,11 +204,11 @@ object Settings:
192204
def setOutput(argValue: String, args: List[String]) =
193205
val path = Directory(argValue)
194206
val isJar = path.ext.isJar
195-
if (!isJar && !path.isDirectory) then
207+
if !isJar && !path.isDirectory then
196208
state.fail(s"'$argValue' does not exist or is not a directory or .jar file")
197209
else
198210
/* Side effect, do not change this method to evaluate eagerly */
199-
def output = if (isJar) JarArchive.create(path) else new PlainDirectory(path)
211+
def output = if isJar then JarArchive.create(path) else new PlainDirectory(path)
200212
update(output, argValue, args)
201213

202214
def setVersion(argValue: String, args: List[String]) =
@@ -212,22 +224,28 @@ object Settings:
212224
case _ => update(strings, argValue, args)
213225

214226
def doSet(argRest: String) =
215-
((summon[ClassTag[T]], args): @unchecked) match
216-
case (BooleanTag, _) =>
227+
classTag[T] match
228+
case BooleanTag =>
217229
if sstate.wasChanged(idx) && preferPrevious then ignoreValue(args)
218230
else setBoolean(argRest, args)
219-
case (OptionTag, _) =>
231+
case OptionTag =>
220232
update(Some(propertyClass.get.getConstructor().newInstance()), "", args)
221-
case (ct, args) =>
233+
case ct =>
222234
val argInArgRest = !argRest.isEmpty || legacyArgs
223-
val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-"))
235+
inline def argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-"))
224236
if argInArgRest then
225237
doSetArg(argRest, args)
226238
else if argAfterParam then
227239
doSetArg(args.head, args.tail)
228-
else missingArg
240+
else if isEmptyDefault then
241+
missingArg
242+
else
243+
doSetArg(arg = null, args)
229244

230-
def doSetArg(arg: String, argsLeft: List[String]) = summon[ClassTag[T]] match
245+
def doSetArg(arg: String, argsLeft: List[String]) =
246+
classTag[T] match
247+
case ListTag if arg == null =>
248+
update(List(default), arg, argsLeft)
231249
case ListTag =>
232250
val strings = arg.split(",").toList
233251
appendList(strings, arg, argsLeft)
@@ -283,6 +301,7 @@ object Settings:
283301
object Setting:
284302
extension [T](setting: Setting[T])
285303
def value(using Context): T = setting.valueIn(ctx.settingsState)
304+
def userValue(using Context): Option[T] = setting.userValueIn(ctx.settingsState)
286305
def update(x: T)(using Context): SettingsState = setting.updateIn(ctx.settingsState, x)
287306
def isDefault(using Context): Boolean = setting.isDefaultIn(ctx.settingsState)
288307

@@ -412,7 +431,7 @@ object Settings:
412431
publish(Setting(category, prependName(name), descr, default, legacyArgs = legacyArgs, deprecation = deprecation))
413432

414433
def OptionSetting[T: ClassTag](category: SettingCategory, name: String, descr: String, aliases: List[String] = Nil, deprecation: Option[Deprecation] = None): Setting[Option[T]] =
415-
publish(Setting(category, prependName(name), descr, None, propertyClass = Some(summon[ClassTag[T]].runtimeClass), aliases = aliases, deprecation = deprecation))
434+
publish(Setting(category, prependName(name), descr, None, propertyClass = Some(classTag[T].runtimeClass), aliases = aliases, deprecation = deprecation))
416435

417436
end SettingGroup
418437
end Settings

0 commit comments

Comments
 (0)