Skip to content

Commit

Permalink
Implement filtering messages based on source file name patterns
Browse files Browse the repository at this point in the history
Also some minor usability improvements.
  • Loading branch information
lrytz committed Sep 9, 2019
1 parent 5edc810 commit 9c37e5e
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 19 deletions.
24 changes: 19 additions & 5 deletions src/compiler/scala/tools/nsc/Reporting.scala
Expand Up @@ -38,7 +38,13 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
class PerRunReporting extends PerRunReportingBase {
lazy val wconf = WConf.parse(settings.Wconf.value) match {
case Left(msgs) =>
globalError(s"Failed to parse `-Wconf` configuration: ${settings.Wconf.value}\n${msgs.mkString("\n")}")
val multiHelp =
if (settings.Wconf.value.exists(_.contains(",")))
"""
|Note: for multiple filters, use `-Wconf:filter1:action1,filter2:action2` (recommended)
| or alternatively `-Wconf filter1:action1 filter2:action2`""".stripMargin
else ""
globalError(s"Failed to parse `-Wconf` configuration: ${settings.Wconf.value}\n${msgs.mkString("\n")}$multiHelp")
WConf(Nil)
case Right(c) => c
}
Expand Down Expand Up @@ -447,10 +453,13 @@ object Reporting {
}

final case class SourcePattern(pattern: Regex) extends MessageFilter {
def matches(message: Message): Boolean = {
// TODO: message.pos.source.file, relativize, check
true
}
private[this] val cache = mutable.Map.empty[SourceFile, Boolean]

def matches(message: Message): Boolean = cache.getOrElseUpdate(message.pos.source, {
var sourcePath = message.pos.source.file.canonicalPath.replace("\\", "/")
if (!sourcePath.startsWith("/")) sourcePath = "/" + sourcePath
pattern.findFirstIn(sourcePath).nonEmpty
})
}

final case class DeprecatedOrigin(pattern: Regex) extends MessageFilter {
Expand Down Expand Up @@ -529,6 +538,11 @@ object Reporting {
case (pv: ParseableVersion, o) => Right(DeprecatedSince(o, pv))
}
}
} else if (s.startsWith("src=")) {
var pat = s.substring(4)
if (!pat.startsWith("/")) pat = "/" + pat
if (!pat.endsWith("$")) pat = pat + "$"
regex(pat).map(SourcePattern)
} else {
Left(s"filter not yet implemented: $s")
}
Expand Down
41 changes: 28 additions & 13 deletions src/compiler/scala/tools/nsc/settings/Warnings.scala
Expand Up @@ -37,14 +37,32 @@ trait Warnings {
s"""Configure compiler warnings.
|Syntax: -Wconf:<filter>&...&<filter>:<action>,<filter>:<action>,...
|
|Note: Run with `-Wconf:any:warning-verbose` to print warnings with their the category,
| site, origin (for deprecations) and since version (for deprecations).
|
|<filter>
| - any message: any
| - message categories: cat=deprecation, cat=lint, cat=lint-infer-any
| - message content (prefix / suffix are skipped): msg=regex
| - site where the warning is triggered (regex must cover full name): site=my.package.*
| - source file name (regex must cover full relative path): src=src_managed/*
| - origin of deprecation (regex must cover full name): origin=external.package.*
| - since of deprecation (operators: <, =, >, versions: n, n.m, n.m.p): since<1.24
| - Any message: any
|
| - Message categories: cat=deprecation, cat=lint, cat=lint-infer-any
| The full list of warning categories is shown at the end of this help text.
|
| - Message content: msg=regex
| The regex must only match a part of the message, not necessarily the full message.
|
| - Site where the warning is triggered: site=my.package.*
| The regex must match the full name of the warning position.
|
| - Source file name: src=src_managed/.*
| The regex must match the canonical path, starting at any path segment.
| Example: `b/.*Test.scala` matches `/a/b/XTest.scala` but not `/ab/YTest.scala`.
|
| - Origin of deprecation: origin=external.package.*
| The regex must match the full name of the deprecated entity.
|
| - Since of deprecation: since<1.24
| Valid operators: <, =, >, valid versions: N, N.M, N.M.P. Compares against the first
| version of the form N, N.M or N.M.P found in the `since` parameter of the deprecation,
| for example `1.2.3` in `@deprecated("", "some lib 1.2.3-foo")`.
|
|<action>
| - error / e
Expand All @@ -62,18 +80,15 @@ trait Warnings {
|User-defined configurations are added to the left. The leftmost rule matching
|a warning message defines the action.
|
|With `-Wconf:any:warning-verbose`, the compiler prints category, site, origin and since
|of every warning to help writing filters.
|
|Examples:
| - change every warning into an error: -Wconf any:error
| - ignore certain deprecations: -Wconf:origin=some.lib.*&since<2.2:s
| - change every warning into an error: -Wconf:any:error
| - silence certain deprecations: -Wconf:origin=some.lib.*&since>2.2:s
|
|Full list of message categories:
|${WarningCategory.all.keys.groupBy(_.split('-').head).toList.sortBy(_._1).map(_._2.toList.sorted.mkString(", ")).mkString(" - ", "\n - ", "")}
|
|Note: on the command-line you might need to quote configurations containing `*` or `&`
|to prevent the shell from expanding it to a list of files in the current directory.""".stripMargin),
|to prevent the shell from expanding patterns.""".stripMargin),
prepend = true)
locally { Wconf.tryToSet(WconfDefault) }

Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/wconfSource1.check
@@ -0,0 +1,7 @@
wconfSource1.scala:10: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
def u = { 1; 2 }
^
wconfSource1.scala:9: error: method dep in class C is deprecated:
def t = dep
^
one error found
11 changes: 11 additions & 0 deletions test/files/neg/wconfSource1.scala
@@ -0,0 +1,11 @@
// scalac: -Wconf:src=.*Source.*&cat=deprecation:e,src=Source1.scala&msg=statement:e,src=wconfSource1&msg=statement:e,src=wconfSource1.scala&msg=statement:i

// src=Source1.scala doesn't match: the pattern needs to start at a path segment (after `/`)
// src=wconfSource1 doesn't match: the pattern needs to match to the end of the path (.scala)
// src=wconfSource1.scala matches

class C {
@deprecated("", "") def dep = 0
def t = dep
def u = { 1; 2 }
}
15 changes: 15 additions & 0 deletions test/files/neg/wconfSource2.check
@@ -0,0 +1,15 @@
wconfSource2.scala:6: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
def u = { 1; 2 }
^
wconfSource2.scala:7: reflective access of structural type member method f should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scaladoc for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
def v(a: { def f: Int }) = a.f
^
wconfSource2.scala:5: error: method dep in class C is deprecated:
def t = dep
^
one error found
8 changes: 8 additions & 0 deletions test/files/neg/wconfSource2.scala
@@ -0,0 +1,8 @@
// scalac: -Wconf:src=test/files/neg/wconfSource2.scala&cat=deprecation:e,src=test/.*&msg=statement:i,src=/neg/.*&cat=feature-reflective-calls:i

class C {
@deprecated("", "") def dep = 0
def t = dep
def u = { 1; 2 }
def v(a: { def f: Int }) = a.f
}
25 changes: 24 additions & 1 deletion test/junit/scala/tools/nsc/reporters/WConfTest.scala
Expand Up @@ -15,10 +15,14 @@ package reporters

import org.junit.Assert.{assertEquals, assertTrue}
import org.junit.Test
import java.io.{File => JFile}

import scala.collection.mutable.ListBuffer
import scala.reflect.internal.util.{BatchSourceFile, Position}
import scala.reflect.internal.{Reporter => InternalReporter}
import scala.tools.nsc.Reporting.Version
import scala.reflect.io.PlainFile
import scala.tools.nsc.Reporting.{Version, WarningCategory}
import scala.reflect.io.File
import scala.tools.nsc.reporters.StoreReporter.Info
import scala.tools.testkit.BytecodeTesting
import scala.tools.testkit.BytecodeTesting._
Expand Down Expand Up @@ -307,4 +311,23 @@ class WConfTest extends BytecodeTesting {
check(v123, v124, ne)
check(v123, v124, ng)
}

@Test
def sourcePatternTest(): Unit = {
def m(p: String) = Reporting.Message.Plain(
Position.offset(new BatchSourceFile(new PlainFile(new File(new JFile(p))), Array()), 0),
msg = "",
WarningCategory.Other,
site = "")

val aTest = Reporting.WConf.parseFilter("src=a/.*Test.scala").getOrElse(null)
assertTrue(aTest.matches(m("/x/a/FooTest.scala")))
assertTrue(aTest.matches(m("c:\\my user\\a\\Test.scala")))
assertTrue(aTest.matches(m("a/NoTest.scala")))
assertTrue(!aTest.matches(m("mama/SomeTest.scala")))
assertTrue(!aTest.matches(m("/x/mama/Test.scala")))

val noScala = Reporting.WConf.parseFilter("src=a/.*Test").getOrElse(null)
assertTrue(!noScala.matches(m("/x/a/FooTest.scala")))
}
}

0 comments on commit 9c37e5e

Please sign in to comment.