Skip to content

Commit

Permalink
Add a -rootdir setting, use for source file Wconf filters
Browse files Browse the repository at this point in the history
The `-rootdir` should be an absolute of to the project root directory.
When defined, the `src=<pattern>` filters in `-Wconf` matches the source
file path relative to the root directory.
  • Loading branch information
lrytz committed Sep 23, 2019
1 parent cac7dcc commit 174a43e
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 19 deletions.
29 changes: 19 additions & 10 deletions src/compiler/scala/tools/nsc/Reporting.scala
Expand Up @@ -20,6 +20,7 @@ import scala.collection.mutable
import scala.reflect.internal.Symbols
import scala.reflect.internal.util.{Position, RangePosition, SourceFile}
import scala.reflect.internal.util.StringOps.countElementsAsString
import scala.reflect.io.AbstractFile
import scala.tools.nsc.Reporting.Version.{NonParseableVersion, ParseableVersion}
import scala.tools.nsc.Reporting._
import scala.util.matching.Regex
Expand All @@ -36,7 +37,10 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
// a new instance of this class is created for every Run (access the current instance via `runReporting`)
protected def PerRunReporting = new PerRunReporting
class PerRunReporting extends PerRunReportingBase {
lazy val wconf = WConf.parse(settings.Wconf.value) match {
private val rootDirPrefix =
if (settings.rootdir.value.isEmpty) ""
else new java.io.File(settings.rootdir.value).getCanonicalPath.replace("\\", "/")
lazy val wconf = WConf.parse(settings.Wconf.value, rootDirPrefix) match {
case Left(msgs) =>
val multiHelp =
if (settings.Wconf.value.exists(_.contains(",")))
Expand Down Expand Up @@ -458,8 +462,8 @@ object Reporting {
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
val sourcePath = message.pos.source.file.canonicalPath.replace("\\", "/")
println(sourcePath)
pattern.findFirstIn(sourcePath).nonEmpty
})
}
Expand Down Expand Up @@ -510,7 +514,7 @@ object Reporting {
try Right(s.r)
catch { case e: PatternSyntaxException => Left(s"invalid pattern `$s`: ${e.getMessage}") }

def parseFilter(s: String): Either[String, MessageFilter] = {
def parseFilter(s: String, rootDir: String): Either[String, MessageFilter] = {
if (s == "any") {
Right(Any)
} else if (s.startsWith("msg=")) {
Expand Down Expand Up @@ -541,16 +545,21 @@ object Reporting {
}
}
} else if (s.startsWith("src=")) {
var pat = s.substring(4)
if (!pat.startsWith("/")) pat = "/" + pat
if (!pat.endsWith("$")) pat = pat + "$"
regex(pat).map(SourcePattern)
val arg = s.substring(4)
var pat = new mutable.StringBuilder()
if (rootDir.nonEmpty) pat += '^' ++= rootDir
// Also prepend prepend a `/` if rootDir is empty, the pattern has to match
// the beginning of a path segment
if (!rootDir.endsWith("/") && !arg.startsWith("/")) pat += '/'
pat ++= arg
if (!arg.endsWith("$")) pat += '$'
regex(pat.toString).map(SourcePattern)
} else {
Left(s"filter not yet implemented: $s")
}
}

def parse(setting: List[String]): Either[List[String], WConf] = {
def parse(setting: List[String], rootDir: String): Either[List[String], WConf] = {
def parseAction(s: String): Either[List[String], Action] = s match {
case "error" | "e" => Right(Error)
case "warning" | "w" => Right(Warning)
Expand All @@ -567,7 +576,7 @@ object Reporting {
else {
val parsedConfs: List[Either[List[String], (List[MessageFilter], Action)]] = setting.map(conf => {
val parts = conf.split("[&:]") // TODO: don't split on escaped \&
val (ms, fs) = parts.view.init.map(parseFilter).toList.partitionMap(identity)
val (ms, fs) = parts.view.init.map(parseFilter(_, rootDir)).toList.partitionMap(identity)
if (ms.nonEmpty) Left(ms)
else if (fs.isEmpty) Left(List("no filters or no action defined"))
else parseAction(parts.last).map((fs, _))
Expand Down
Expand Up @@ -34,6 +34,7 @@ trait StandardScalaSettings {
val javabootclasspath = PathSetting ("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath) withAbbreviation "--java-boot-class-path"
val javaextdirs = PathSetting ("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs) withAbbreviation "--java-extension-directories"
val sourcepath = PathSetting ("-sourcepath", "Specify location(s) of source files.", "") withAbbreviation "--source-path" // Defaults.scalaSourcePath
val rootdir = PathSetting ("-rootdir", "The absolute path of the project root directory, usually the git / scm checkout.", "") withAbbreviation "--root-directory"

/** Other settings.
*/
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/scala/tools/nsc/settings/Warnings.scala
Expand Up @@ -54,8 +54,10 @@ trait Warnings {
| 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`.
| If `-rootdir` is specified, the regex must match the canonical path relative to the
| root directory. Otherwise, the regex must match the canonical path relative to any
| path segment (`b/.*Test.scala` matches `/a/b/XTest.scala` but not `/ab/Test.scala`).
| Use unix-style paths, separated by `/`.
|
| - Origin of deprecation: origin=external.package.*
| The regex must match the full name of the deprecated entity.
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -3844,7 +3844,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val filters = info.assocs match {
case Nil => List(MessageFilter.Any)
case (_, LiteralAnnotArg(s)) :: Nil =>
val (ms, fs) = s.stringValue.split('&').map(WConf.parseFilter).toList.partitionMap(identity)
val (ms, fs) = s.stringValue.split('&').map(WConf.parseFilter(_, settings.rootdir.value)).toList.partitionMap(identity)
if (ms.nonEmpty)
reporter.error(info.pos, s"Invalid message filter:\n${ms.mkString("\n")}")
fs
Expand Down
27 changes: 21 additions & 6 deletions test/junit/scala/tools/nsc/reporters/WConfTest.scala
Expand Up @@ -315,19 +315,34 @@ class WConfTest extends BytecodeTesting {
@Test
def sourcePatternTest(): Unit = {
def m(p: String) = Reporting.Message.Plain(
Position.offset(new BatchSourceFile(new PlainFile(new File(new JFile(p))), Array()), 0),
Position.offset(new BatchSourceFile(new PlainFile(new File(new JFile(p))) {
override lazy val canonicalPath: String = 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")))
val aTest = Reporting.WConf.parseFilter("src=a/.*Test.scala", rootDir = "").getOrElse(null)
assertTrue(aTest.matches(m("/a/FooTest.scala")))
assertTrue(aTest.matches(m("/x/a/b/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)
val noScala = Reporting.WConf.parseFilter("src=a/.*Test", rootDir = "").getOrElse(null)
assertTrue(!noScala.matches(m("/x/a/FooTest.scala")))

val withRootDir = Reporting.WConf.parseFilter("src=a/.*Test.scala", rootDir = "/root/path").getOrElse(null)
assertTrue(withRootDir.matches(m("/root/path/a/FooTest.scala")))
assertTrue(withRootDir.matches(m("/root/path/a/b/c/FooTest.scala")))
assertTrue(!withRootDir.matches(m("/root/path/xa/FooTest.scala")))
assertTrue(!withRootDir.matches(m("/root/a/FooTest.scala")))
assertTrue(!withRootDir.matches(m("/root/path/b/a/FooTest.scala")))

val withRootDirWin = Reporting.WConf.parseFilter("src=a/.*Test.scala", rootDir = "c:/root/path").getOrElse(null)
assertTrue(withRootDirWin.matches(m("c:/root/path/a/FooTest.scala")))
assertTrue(withRootDirWin.matches(m("c:/root/path/a/b/c/FooTest.scala")))
assertTrue(!withRootDirWin.matches(m("c:/root/path/xa/FooTest.scala")))
assertTrue(!withRootDirWin.matches(m("c:/root/a/FooTest.scala")))
assertTrue(!withRootDirWin.matches(m("c:/root/path/b/a/FooTest.scala")))
}
}

0 comments on commit 174a43e

Please sign in to comment.