diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index e58cff7eb130..be8135c2367a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -248,6 +248,9 @@ private sealed trait WarningSettings: | - Message name: name=PureExpressionInStatementPosition | The message name is printed with the warning in verbose warning mode. | + | - Source location: src=regex + | The regex is evaluated against the full source path. + | |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`). @@ -267,6 +270,7 @@ private sealed trait WarningSettings: |Examples: | - change every warning into an error: -Wconf:any:error | - silence deprecations: -Wconf:cat=deprecation: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 `&` |to prevent the shell from expanding patterns.""".stripMargin, diff --git a/compiler/src/dotty/tools/dotc/reporting/WConf.scala b/compiler/src/dotty/tools/dotc/reporting/WConf.scala index 1086e7368877..6766af1e46f4 100644 --- a/compiler/src/dotty/tools/dotc/reporting/WConf.scala +++ b/compiler/src/dotty/tools/dotc/reporting/WConf.scala @@ -5,7 +5,9 @@ package reporting import scala.language.unsafeNulls import dotty.tools.dotc.core.Contexts.* -import dotty.tools.dotc.util.SourcePosition +import dotty.tools.dotc.util.{NoSourcePosition, SourcePosition} +import dotty.tools.dotc.interfaces.SourceFile +import dotty.tools.dotc.reporting.MessageFilter.SourcePattern import java.util.regex.PatternSyntaxException import scala.annotation.internal.sharable @@ -21,11 +23,19 @@ enum MessageFilter: 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 None => false case Any, Deprecated, Feature, Unchecked, None case MessagePattern(pattern: Regex) case MessageID(errorId: ErrorMessageID) + case SourcePattern(pattern: Regex) enum Action: case Error, Warning, Verbose, Info, Silent @@ -84,6 +94,9 @@ object WConf: case "feature" => Right(Feature) case "unchecked" => Right(Unchecked) case _ => Left(s"unknown category: $conf") + + case "src" => regex(conf).map(SourcePattern.apply) + case _ => Left(s"unknown filter: $filter") case _ => Left(s"unknown filter: $s") diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 44cf83b521f4..c36823dce987 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -5,8 +5,14 @@ import CommandLineParser.tokenize import Settings._ import org.junit.Test +import org.junit.Ignore import org.junit.Assert._ import core.Decorators.toMessage +import dotty.tools.io.{Path, PlainFile} + +import java.net.URI +import java.nio.file.Files +import scala.util.Using class ScalaSettingsTests: @@ -83,4 +89,115 @@ class ScalaSettingsTests: val nowr = new Diagnostic.Warning("This is a problem.".toMessage, util.NoSourcePosition) assertEquals(Action.Silent, sut.action(nowr)) + @Ignore("LTS backport rejected: https://github.com/scala/scala3/pull/18503") + @Test def `i18367 rightmost WConf flags take precedence over flags to the left`: Unit = + import reporting.{Action, Diagnostic} + 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) + val conf = sets.Wconf.valueIn(proc.sstate) + val msg = "Don't use that!".toMessage + val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition) + val sut = reporting.WConf.fromSettings(conf).getOrElse(???) + assertEquals(Action.Silent, sut.action(depr)) + + + private def wconfSrcFilterTest(argsStr: String, + warning: reporting.Diagnostic.Warning): Either[List[String], reporting.Action] = + import reporting.Diagnostic + val settings = new ScalaSettings + val args = ArgsSummary(settings.defaultState, List(argsStr), errors = Nil, warnings = Nil) + val proc = settings.processArguments(args, processAll = true, skipped = Nil) + val wconfStr = settings.Wconf.valueIn(proc.sstate) + val wconf = reporting.WConf.fromSettings(wconfStr) + wconf.map(_.action(warning)) + + @Test def `WConf src filter silences warnings from a matching path for virtual file`: Unit = + val result = wconfSrcFilterTest( + argsStr = "-Wconf:src=path/.*:s", + warning = reporting.Diagnostic.Warning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""), + span = util.Spans.Span(1L) + ) + ) + ) + assertEquals(result, Right(reporting.Action.Silent)) + + @Test def `WConf src filter doesn't silence warnings from a non-matching path`: Unit = + val result = wconfSrcFilterTest( + argsStr = "-Wconf:src=another/.*:s", + warning = reporting.Diagnostic.Warning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""), + span = util.Spans.Span(1L) + ) + ) + ) + assertEquals(result, Right(reporting.Action.Warning)) + + @Test def `WConf src filter silences warnings from a matching path for real file`: Unit = + val result = Using.resource(Files.createTempFile("myfile", ".scala").nn) { file => + wconfSrcFilterTest( + argsStr = "-Wconf:src=myfile.*?\\.scala:s", + warning = reporting.Diagnostic.Warning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile(new PlainFile(Path(file)), "UTF-8"), + span = util.Spans.Span(1L) + ) + ) + ) + }(Files.deleteIfExists(_)) + assertEquals(result, Right(reporting.Action.Silent)) + + @Test def `WConf src filter doesn't silence warnings from a non-matching path for real file`: Unit = + val result = Using.resource(Files.createTempFile("myfile", ".scala").nn) { file => + wconfSrcFilterTest( + argsStr = "-Wconf:src=another.*?\\.scala:s", + warning = reporting.Diagnostic.Warning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile(new PlainFile(Path(file)), "UTF-8"), + span = util.Spans.Span(1L) + ) + ) + ) + }(Files.deleteIfExists(_)) + assertEquals(result, Right(reporting.Action.Warning)) + + @Test def `WConf src filter reports an error on an invalid regex`: Unit = + val result = wconfSrcFilterTest( + argsStr = """-Wconf:src=\:s""", + warning = reporting.Diagnostic.Warning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""), + span = util.Spans.Span(1L) + ) + ), + ) + assertTrue( + result.left.exists(errors => + errors.sizeIs == 1 && errors.headOption.exists(_.startsWith("invalid pattern")) + ) + ) + + @Ignore("LTS backport rejected: https://github.com/scala/scala3/pull/18503") + @Test def `WConf src filter can be mixed with other filters with rightmost taking precedence`: Unit = + val result = wconfSrcFilterTest( + argsStr = "-Wconf:src=.*:s,cat=deprecation:e", + warning = reporting.Diagnostic.DeprecationWarning( + "A warning".toMessage, + util.SourcePosition( + source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""), + span = util.Spans.Span(1L) + ) + ) + ) + assertEquals(result, Right(reporting.Action.Error)) + end ScalaSettingsTests