From 753e9d72528e9cc6e4ead5212ff80bd8b5affade Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 20 Aug 2024 13:03:33 -0700 Subject: [PATCH 1/3] Add origin filter to WConf, DeprecationWarning [Cherry-picked 69530560f6630e1a6912dc6280d79107037792b1] --- compiler/src/dotty/tools/dotc/report.scala | 4 ++-- .../dotty/tools/dotc/reporting/Diagnostic.scala | 3 ++- .../src/dotty/tools/dotc/reporting/WConf.scala | 9 +++++++-- .../tools/dotc/typer/CrossVersionChecks.scala | 4 ++-- .../src/dotty/tools/dotc/typer/RefChecks.scala | 4 +++- .../tools/dotc/config/ScalaSettingsTests.scala | 7 ++++--- tests/warn/deprecated-origin.scala | 15 +++++++++++++++ 7 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 tests/warn/deprecated-origin.scala diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 1d8ca5f208fa..5eac9fc38da2 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -23,8 +23,8 @@ object report: private def issueWarning(warning: Warning)(using Context): Unit = ctx.reporter.report(warning) - def deprecationWarning(msg: Message, pos: SrcPos)(using Context): Unit = - issueWarning(new DeprecationWarning(msg, pos.sourcePos)) + def deprecationWarning(msg: Message, pos: SrcPos, origin: String = "")(using Context): Unit = + issueWarning(new DeprecationWarning(msg, pos.sourcePos, origin)) def migrationWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new MigrationWarning(msg, pos.sourcePos)) diff --git a/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala index 7a8edb233aee..6a2d88f4e82f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala @@ -75,7 +75,8 @@ object Diagnostic: class DeprecationWarning( msg: Message, - pos: SourcePosition + pos: SourcePosition, + val origin: String ) extends ConditionalWarning(msg, pos) { def enablingOption(using Context): Setting[Boolean] = ctx.settings.deprecation } diff --git a/compiler/src/dotty/tools/dotc/reporting/WConf.scala b/compiler/src/dotty/tools/dotc/reporting/WConf.scala index 54a6fc14e054..1896e5269d6c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/WConf.scala +++ b/compiler/src/dotty/tools/dotc/reporting/WConf.scala @@ -19,23 +19,27 @@ enum MessageFilter: case Deprecated => message.isInstanceOf[Diagnostic.DeprecationWarning] case Feature => message.isInstanceOf[Diagnostic.FeatureWarning] case Unchecked => message.isInstanceOf[Diagnostic.UncheckedWarning] + case MessageID(errorId) => message.msg.errorId == errorId case MessagePattern(pattern) => val noHighlight = message.msg.message.replaceAll("\\e\\[[\\d;]*[^\\d;]","") pattern.findFirstIn(noHighlight).nonEmpty - case MessageID(errorId) => message.msg.errorId == errorId case SourcePattern(pattern) => val source = message.position.orElse(NoSourcePosition).source() val path = source.jfile() .map(_.toPath.toAbsolutePath.toUri.normalize().getRawPath) .orElse(source.path()) pattern.findFirstIn(path).nonEmpty - + case Origin(pattern) => + message match + case message: Diagnostic.DeprecationWarning => pattern.findFirstIn(message.origin).nonEmpty + case _ => false case None => false case Any, Deprecated, Feature, Unchecked, None case MessagePattern(pattern: Regex) case MessageID(errorId: ErrorMessageID) case SourcePattern(pattern: Regex) + case Origin(pattern: Regex) enum Action: case Error, Warning, Verbose, Info, Silent @@ -96,6 +100,7 @@ object WConf: case _ => Left(s"unknown category: $conf") case "src" => regex(conf).map(SourcePattern.apply) + case "origin" => regex(conf).map(Origin.apply) case _ => Left(s"unknown filter: $filter") case _ => Left(s"unknown filter: $s") diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 5ce1b02733d0..6020431672b9 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -78,7 +78,7 @@ class CrossVersionChecks extends MiniPhase: do val msg = annot.argumentConstantString(0).map(msg => s": $msg").getOrElse("") val since = annot.argumentConstantString(1).map(version => s" (since: $version)").getOrElse("") - report.deprecationWarning(em"inheritance from $psym is deprecated$since$msg", parent.srcPos) + report.deprecationWarning(em"inheritance from $psym is deprecated$since$msg", parent.srcPos, origin=psym.showFullName) } override def transformValDef(tree: ValDef)(using Context): ValDef = @@ -171,7 +171,7 @@ object CrossVersionChecks: def maybeWarn(annotee: Symbol, annot: Annotation) = if !skipWarning(sym) then val message = annot.argumentConstantString(0).filter(!_.isEmpty).map(": " + _).getOrElse("") val since = annot.argumentConstantString(1).filter(!_.isEmpty).map(" since " + _).getOrElse("") - report.deprecationWarning(em"${annotee.showLocated} is deprecated${since}${message}", pos) + report.deprecationWarning(em"${annotee.showLocated} is deprecated${since}${message}", pos, origin=annotee.showFullName) sym.getAnnotation(defn.DeprecatedAnnot) match case Some(annot) => maybeWarn(sym, annot) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 42be822c9709..0529d2a35847 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -481,7 +481,9 @@ object RefChecks { def overrideDeprecation(what: String, member: Symbol, other: Symbol, fix: String): Unit = report.deprecationWarning( em"overriding $what${infoStringWithLocation(other)} is deprecated;\n ${infoString(member)} should be $fix.", - if member.owner == clazz then member.srcPos else clazz.srcPos) + if member.owner == clazz then member.srcPos else clazz.srcPos, + origin = other.showFullName + ) def autoOverride(sym: Symbol) = sym.is(Synthetic) && ( diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 3dc4f4e4ec5e..a412848eaa98 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -81,7 +81,7 @@ class ScalaSettingsTests: val conf = sets.Wconf.valueIn(proc.sstate) val sut = reporting.WConf.fromSettings(conf).getOrElse(???) val msg = "There was a problem!".toMessage - val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition) + val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition, origin="") assertEquals(Action.Silent, sut.action(depr)) val feat = new Diagnostic.FeatureWarning(msg, util.NoSourcePosition) assertEquals(Action.Error, sut.action(feat)) @@ -197,7 +197,7 @@ class ScalaSettingsTests: val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) val conf = sets.Wconf.valueIn(proc.sstate) val msg = "Don't use that!".toMessage - val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition) + val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition, origin="") val sut = reporting.WConf.fromSettings(conf).getOrElse(???) assertEquals(Action.Silent, sut.action(depr)) @@ -293,7 +293,8 @@ class ScalaSettingsTests: util.SourcePosition( source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""), span = util.Spans.Span(1L) - ) + ), + origin="", ) ) assertEquals(result, Right(reporting.Action.Error)) diff --git a/tests/warn/deprecated-origin.scala b/tests/warn/deprecated-origin.scala new file mode 100644 index 000000000000..e028515d795c --- /dev/null +++ b/tests/warn/deprecated-origin.scala @@ -0,0 +1,15 @@ +//> using options -deprecation -Wconf:origin=p\.C$:s + +package p: + @deprecated("Old style", since="1.0") + class C + @deprecated("Bad style", since="1.0") + class Crude + +package q: + import annotation.* + import p.* + class D extends C // nowarn - C$ pattern avoids matching Crude + class Oil extends Crude // warn + @nowarn("""origin=p\.Crude""") + class Language extends Crude // nowarn obvs From fc1aae65ef52228e8645ca0d451e5a1a8e90d55e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 24 Aug 2024 09:39:04 -0700 Subject: [PATCH 2/3] More doc [Cherry-picked 3c873cf495412a83d1f0dfc960c343c311db4a5c] --- .../tools/dotc/config/ScalaSettings.scala | 6 ++- .../dotc/reporting/MessageRendering.scala | 39 +++++++++++-------- tests/warn/deprecated-origin-verbose.check | 14 +++++++ tests/warn/deprecated-origin-verbose.scala | 15 +++++++ 4 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 tests/warn/deprecated-origin-verbose.check create mode 100644 tests/warn/deprecated-origin-verbose.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index d775a4239d1b..72a051ea8154 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -233,7 +233,7 @@ private sealed trait WarningSettings: "patterns", default = List(), descr = - s"""Configure compiler warnings. + raw"""Configure compiler warnings. |Syntax: -Wconf::,:,... |multiple are combined with &, i.e., &...& | @@ -254,6 +254,9 @@ private sealed trait WarningSettings: | - Source location: src=regex | The regex is evaluated against the full source path. | + | - Origin of warning: origin=regex + | The regex must match the full name (`package.Class.method`) of the deprecated entity. + | |In verbose warning mode the compiler prints matching filters for warnings. |Verbose mode can be enabled globally using `-Wconf:any:verbose`, or locally |using the @nowarn annotation (example: `@nowarn("v") def test = try 1`). @@ -273,6 +276,7 @@ private sealed trait WarningSettings: |Examples: | - change every warning into an error: -Wconf:any:error | - silence deprecations: -Wconf:cat=deprecation:s + | - silence a deprecation: -Wconf:origin=java\.lang\.Thread\.getId:s | - silence warnings in src_managed directory: -Wconf:src=src_managed/.*:s | |Note: on the command-line you might need to quote configurations containing `*` or `&` diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 6881235e3dc1..7db5112b6674 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -15,7 +15,7 @@ import util.{ SourcePosition, NoSourcePosition } import util.Chars.{ LF, CR, FF, SU } import scala.annotation.switch -import scala.collection.mutable +import scala.collection.mutable.StringBuilder trait MessageRendering { import Highlight.* @@ -209,22 +209,27 @@ trait MessageRendering { sb.toString } - private def appendFilterHelp(dia: Diagnostic, sb: mutable.StringBuilder): Unit = - import dia.* + private def appendFilterHelp(dia: Diagnostic, sb: StringBuilder): Unit = + import dia.msg val hasId = msg.errorId.errorNumber >= 0 - val category = dia match { - case _: UncheckedWarning => "unchecked" - case _: DeprecationWarning => "deprecation" - case _: FeatureWarning => "feature" - case _ => "" - } - if (hasId || category.nonEmpty) - sb.append(EOL).append("Matching filters for @nowarn or -Wconf:") - if (hasId) - sb.append(EOL).append(" - id=E").append(msg.errorId.errorNumber) - sb.append(EOL).append(" - name=").append(msg.errorId.productPrefix.stripSuffix("ID")) - if (category.nonEmpty) - sb.append(EOL).append(" - cat=").append(category) + val (category, origin) = dia match + case _: UncheckedWarning => ("unchecked", "") + case w: DeprecationWarning => ("deprecation", w.origin) + case _: FeatureWarning => ("feature", "") + case _ => ("", "") + var entitled = false + def addHelp(what: String)(value: String): Unit = + if !entitled then + sb.append(EOL).append("Matching filters for @nowarn or -Wconf:") + entitled = true + sb.append(EOL).append(" - ").append(what).append(value) + if hasId then + addHelp("id=E")(msg.errorId.errorNumber.toString) + addHelp("name=")(msg.errorId.productPrefix.stripSuffix("ID")) + if category.nonEmpty then + addHelp("cat=")(category) + if origin.nonEmpty then + addHelp("origin=")(origin) /** The whole message rendered from `msg` */ def messageAndPos(dia: Diagnostic)(using Context): String = { @@ -236,7 +241,7 @@ trait MessageRendering { else 0 given Level = Level(level) given Offset = Offset(maxLineNumber.toString.length + 2) - val sb = mutable.StringBuilder() + val sb = StringBuilder() val posString = posStr(pos, msg, diagnosticLevel(dia)) if (posString.nonEmpty) sb.append(posString).append(EOL) if (pos.exists) { diff --git a/tests/warn/deprecated-origin-verbose.check b/tests/warn/deprecated-origin-verbose.check new file mode 100644 index 000000000000..e67efaf8668d --- /dev/null +++ b/tests/warn/deprecated-origin-verbose.check @@ -0,0 +1,14 @@ +-- Deprecation Warning: tests/warn/deprecated-origin-verbose.scala:12:18 ----------------------------------------------- +12 | class D extends C // warn + | ^ + | class C in package p is deprecated since 1.0: Old style +Matching filters for @nowarn or -Wconf: + - cat=deprecation + - origin=p.C +-- Deprecation Warning: tests/warn/deprecated-origin-verbose.scala:13:20 ----------------------------------------------- +13 | class Oil extends Crude // warn + | ^^^^^ + | class Crude in package p is deprecated since 1.0: Bad style +Matching filters for @nowarn or -Wconf: + - cat=deprecation + - origin=p.Crude diff --git a/tests/warn/deprecated-origin-verbose.scala b/tests/warn/deprecated-origin-verbose.scala new file mode 100644 index 000000000000..0a960744bdcf --- /dev/null +++ b/tests/warn/deprecated-origin-verbose.scala @@ -0,0 +1,15 @@ +//> using options -deprecation -Wconf:any:verbose + +package p: + @deprecated("Old style", since="1.0") + class C + @deprecated("Bad style", since="1.0") + class Crude + +package q: + import annotation.* + import p.* + class D extends C // warn + class Oil extends Crude // warn + @nowarn("""origin=p\.Crude""") + class Language extends Crude // nowarn obvs From 417c9bcbe20a0e700f74e8ecc4156207f004f58d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 24 Aug 2024 14:39:14 -0700 Subject: [PATCH 3/3] Restore help msg for other than language [Cherry-picked 929e7eb814f7236f51b383ea1717a04e93bfa6e5] --- compiler/src/dotty/tools/dotc/config/CompilerCommand.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala index 43f3ed63f969..e90bbcc36878 100644 --- a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala @@ -9,7 +9,8 @@ abstract class CompilerCommand extends CliCommand: final def helpMsg(using settings: ConcreteSettings)(using SettingsState, Context): String = settings.allSettings.find(isHelping) match - case Some(s) => availableOptionsMsg(_ == s, showArgFileMsg = false) + case Some(s @ settings.language) => availableOptionsMsg(_ == s, showArgFileMsg = false) + case Some(s) => s.description case _ => if (settings.help.value) usageMessage else if (settings.Vhelp.value) vusageMessage