From 6c8c97b6f81c9c57e210a2149172a36acd9190f0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 1 Oct 2022 15:05:47 -0700 Subject: [PATCH 1/2] Accept -Vprint:all, -Vprint:~tailcalls --- src/compiler/scala/tools/nsc/Global.scala | 14 +++--- .../tools/nsc/settings/MutableSettings.scala | 15 ++++-- .../tools/nsc/settings/SettingsTest.scala | 48 +++++++++++++++++++ 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 926496d913fb..b63cdf08fdb7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1275,26 +1275,26 @@ class Global(var currentSettings: Settings, reporter0: Reporter) // doesn't select a unique phase, that might be surprising too. def checkPhaseSettings(including: Boolean, specs: Seq[String]*) = { def isRange(s: String) = s.forall(c => c.isDigit || c == '-') - def isSpecial(s: String) = (s == "_" || isRange(s)) + def isMulti(s: String) = s == "_" || s == "all" || isRange(s) || s.startsWith("~") val tester = new ss.PhasesSetting("fake","fake") for (p <- specs.flatten.to(Set)) { tester.value = List(p) val count = if (including) first.iterator.count(tester.containsPhase(_)) - else phaseDescriptors.count(pd => tester.contains(pd.phaseName)) + else phaseDescriptors.count(pd => tester.contains(pd.phaseName) || tester.contains(s"~${pd.phaseName}")) if (count == 0) runReporting.warning(NoPosition, s"'$p' specifies no phase", WarningCategory.Other, site = "") - if (count > 1 && !isSpecial(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") - if (!including && isSpecial(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") + if (count > 1 && !isMulti(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") + if (!including && isMulti(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") tester.clear() } } // phases that are excluded; for historical reasons, these settings only select by phase name val exclusions = List(ss.stopBefore, ss.stopAfter, ss.skip) val inclusions = ss.visibleSettings collect { - case s: ss.PhasesSetting if !(exclusions contains s) => s.value + case s: ss.PhasesSetting if !exclusions.contains(s) => s.value } checkPhaseSettings(including = true, inclusions.toSeq: _*) - checkPhaseSettings(including = false, exclusions map (_.value): _*) + checkPhaseSettings(including = false, exclusions.map(_.value): _*) // Report the overhead of statistics measurements per every run if (settings.areStatisticsEnabled) @@ -1536,7 +1536,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) informTime(globalPhase.description, phaseTimer.nanos) // progress update - if ((settings.Xprint containsPhase globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { + if (settings.Xprint.containsPhase(globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { // print trees if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) nodePrinters.printAll() else printAllUnits() diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 314b5393138f..aa81abb28041 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -945,14 +945,19 @@ class MutableSettings(val errorFn: String => Unit, val pathFactory: PathFactory) def clear(): Unit = (v = Nil) - // we slightly abuse the usual meaning of "contains" here by returning - // true if our phase list contains "_", regardless of the incoming argument + /* True if the named phase is selected. + * + * A setting value "_" or "all" selects all phases by name. + */ def contains(phName: String) = doAllPhases || containsName(phName) - def containsName(phName: String) = stringValues exists (phName startsWith _) + /* True if the given phase name matches the selection, possibly as prefixed "~name". */ + def containsName(phName: String) = stringValues.exists(phName.startsWith(_)) def containsId(phaseId: Int) = phaseIdTest(phaseId) - def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) + /* True if the phase is selected by name or "all", or by id, or by prefixed "~name". */ + def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) || containsName(s"~${ph.name}") || + ph.next != null && containsName(s"~${ph.next.name}") // null if called during construction - def doAllPhases = stringValues.contains("_") + def doAllPhases = stringValues.exists(s => s == "_" || s == "all") def unparse: List[String] = value.map(v => s"$name:$v") withHelpSyntax( diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index 3d63d7fe75c6..ce46108f09db 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -354,6 +354,54 @@ class SettingsTest { assertFalse(s.isInfo) assertTrue(s.printArgs.isSetByUser) } + @Test def `name-based phases setting accepts tilde prefix`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + val chunk = new Phase(start) { def name = "chunker"; def run() = () } + val clean = new Phase(chunk) { def name = "clean"; def run() = () } + val go = new Phase(clean) { def name = "go"; def run() = () } + val end = new Phase(go) { def name = "end"; def run() = () } + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val args = List("-Yp:clean,~chunker,3") + val (ok, residual) = s.processArguments(args, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + assertTrue(ps.contains("clean")) + assertFalse(ps.contains("chunker")) + assertTrue(ps.contains("~chunker")) + assertFalse(ps.contains("start")) + assertFalse(ps.contains("end")) + assertTrue(ps.containsPhase(clean)) + assertTrue(ps.containsPhase(chunk)) + assertTrue(ps.containsPhase(start)) + assertTrue(ps.containsPhase(start.next)) + assertTrue(ps.contains(s"~${start.next.name}")) + assertTrue(ps.containsPhase(go)) + assertTrue(ps.containsId(go.id)) + assertFalse(ps.containsPhase(end)) + assertFalse(ps.containsId(end.id)) + } + @Test def `phases setting accepts all or underscore`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + def check(args: String*): MutableSettings#PhasesSetting = { + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val (ok, residual) = s.processArguments(args.toList, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + ps + } + assertTrue(check("-Yp:start").containsPhase(start)) + assertTrue(check("-Yp:start").contains("start")) + assertTrue(check("-Yp:start").containsName("start")) + assertTrue(check("-Yp:all").containsPhase(start)) + assertTrue(check("-Yp:all").contains("start")) + assertFalse(check("-Yp:all").containsName("start")) + assertTrue(check("-Yp:_").containsPhase(start)) + assertTrue(check("-Yp:junk,_").containsPhase(start)) + } } object SettingsTest { import language.implicitConversions From 33ecb937fe03985d0a4c8ecd9f4829bb0b5c3089 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 31 Oct 2022 10:27:12 -0700 Subject: [PATCH 2/2] Upgrade -Vprint doc and default to typer --- src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6a3a812f21d8..e551cfec3c30 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -499,7 +499,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val showPhases = BooleanSetting("-Vphases", "Print a synopsis of compiler phases.") .withAbbreviation("-Xshow-phases") val Yposdebug = BooleanSetting("-Vpos", "Trace position validation.") withAbbreviation "-Ypos-debug" - val Xprint = PhasesSetting("-Vprint", "Print out program after") + val Xprint = PhasesSetting("-Vprint", "Print out program after (or ~phase for before and after)", "typer") .withAbbreviation("-Xprint") val Xprintpos = BooleanSetting("-Vprint-pos", "Print tree positions, as offsets.") .withAbbreviation("-Xprint-pos")