Skip to content

Commit

Permalink
Ref sbt#520
Browse files Browse the repository at this point in the history
This is an improvement over the status quo of forked Javac error parsing,
but likely not a complete fix.

"Normal" Java error messages look like:

```
/path/a.java:100:1: error message
        if (x.isBar) {
              ^
  symbol: xxx
  location: xxx
```

However, under certain circumstances javac reports compiler errors are reported as:

```
/path/a.java:100:1: error message
symbol: xxx
location: xxx
if (x.isBar) {
```

Current parsing assumes existence of `^` as well as indentation before "symbol" etc.

Ultimately a better way of handling forking probably is to define our own small commandline app, like forked tests are done.
  • Loading branch information
eed3si9n committed Jul 5, 2018
1 parent d61d364 commit 886444c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,33 @@ class JavaErrorParser(relativeDir: File = new File(new File(".").getAbsolutePath
}) ^^ {
case xs => xs.mkString("\n")
}
val nonPathLine: Parser[String] = {
val nonPathLine0 = new Parser[String] {
def isStopChar(c: Char): Boolean = c == '\n' || c == '\r'

def apply(in: Input) = {
val source = in.source
val offset = in.offset
var i = offset
while (i < source.length && !isStopChar(source.charAt(i))) {
i += 1
}
val line = source.subSequence(offset, i).toString
if ((line.startsWith("/") || line.contains("\\")) && line.contains(".java"))
Failure("Path found", in)
else if (i == offset) Failure("Empty", in)
else Success(line, in.drop(i - offset))
}
}
nonPathLine0 ~ """[\r]?[\n]?""".r ^^ {
case msg ~ endline => msg + endline
}
}
val nonPathLines: Parser[String] = {
rep(nonPathLine) ^^ {
case lines => lines.mkString("")
}
}

// Parses ALL characters until an expected character is met.
def allUntilChar(c: Char): Parser[String] = allUntilChars(Array(c))
Expand Down Expand Up @@ -148,15 +175,21 @@ class JavaErrorParser(relativeDir: File = new File(new File(".").getAbsolutePath
val fileLineMessage = fileAndLineNo ~ SEMICOLON ~ restOfLine ^^ {
case (file, line) ~ _ ~ msg => (file, line, msg)
}
fileLineMessage ~ allUntilCaret ~ restOfLine ~ (allIndented.?) ^^ {
case (file, line, msg) ~ contents ~ r ~ ind =>
fileLineMessage ~ (allUntilCaret ~ '^' ~ restOfLine).? ~ (nonPathLines.?) ^^ {
case (file, line, msg) ~ contentsOpt ~ ind =>
new JavaProblem(
new JavaPosition(
findFileSource(file),
line,
contents + '^' + r + ind
(contentsOpt match {
case Some(contents ~ _ ~ r) => contents + '^' + r
case _ => ""
}) + ind
.getOrElse(""), // TODO - Actually parse caret position out of here.
getOffset(contents)
(contentsOpt match {
case Some(contents ~ _ ~ _) => getOffset(contents)
case _ => 0
})
),
Severity.Error,
msg
Expand All @@ -169,14 +202,20 @@ class JavaErrorParser(relativeDir: File = new File(new File(".").getAbsolutePath
val fileLineMessage = fileAndLineNo ~ SEMICOLON ~ WARNING ~ SEMICOLON ~ restOfLine ^^ {
case (file, line) ~ _ ~ _ ~ _ ~ msg => (file, line, msg)
}
fileLineMessage ~ allUntilCaret ~ restOfLine ~ (allIndented.?) ^^ {
case (file, line, msg) ~ contents ~ r ~ ind =>
fileLineMessage ~ (allUntilCaret ~ '^' ~ restOfLine).? ~ (nonPathLines.?) ^^ {
case (file, line, msg) ~ contentsOpt ~ ind =>
new JavaProblem(
new JavaPosition(
findFileSource(file),
line,
contents + "^" + r + ind.getOrElse(""),
getOffset(contents)
(contentsOpt match {
case Some(contents ~ _ ~ r) => contents + '^' + r
case _ => ""
}) + ind.getOrElse(""),
(contentsOpt match {
case Some(contents ~ _ ~ _) => getOffset(contents)
case _ => 0
})
),
Severity.Warn,
msg
Expand All @@ -201,9 +240,15 @@ class JavaErrorParser(relativeDir: File = new File(new File(".").getAbsolutePath
)
}

val outputSumamry: Parser[Unit] =
"""(\s*)(\d+) (\w+)""".r ~ restOfLine ^^ {
case a ~ b =>
()
}

val potentialProblem: Parser[Problem] = warningMessage | errorMessage | noteMessage | javacError

val javacOutput: Parser[Seq[Problem]] = rep(potentialProblem)
val javacOutput: Parser[Seq[Problem]] = rep(potentialProblem) <~ opt(outputSumamry)

/**
* Example:
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ class JavaProcessLoggerSpec extends UnitSpec {
val javacLogger = new JavacLogger(errorLogger, reporter, cwd = new File("."))
javacLogger.err(
Seq(
"""Test.java:4: cannot find symbol
"""/home/someone/Test.java:4: cannot find symbol
|symbol : method baz()
|location: class Foo
|return baz();
| ^
|""",
"""Test.java:8: warning: [deprecation] RMISecurityException(java.lang.String) in java.rmi.RMISecurityException has been deprecated
"""/home/someone/Test.java:8: warning: [deprecation] RMISecurityException(java.lang.String) in java.rmi.RMISecurityException has been deprecated
|throw new java.rmi.RMISecurityException("O NOES");
|^
|"""
Expand All @@ -41,14 +41,14 @@ class JavaProcessLoggerSpec extends UnitSpec {
val reporter = new CollectingReporter()
val errorLogger = new CollectingLogger()
val javacLogger = new JavacLogger(errorLogger, reporter, cwd = new File("."))
javacLogger.err("""Test.java:4: cannot find symbol
javacLogger.err("""/home/someone/Test.java:4: cannot find symbol
|symbol : method baz()
|location: class Foo
|return baz();
| ^
|""")
javacLogger.err(
"""Test.java:8: warning: [deprecation] RMISecurityException(java.lang.String) in java.rmi.RMISecurityException has been deprecated
"""/home/someone/Test.java:8: warning: [deprecation] RMISecurityException(java.lang.String) in java.rmi.RMISecurityException has been deprecated
|throw new java.rmi.RMISecurityException("O NOES");
|^
|""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,35 @@ package internal
package inc
package javac

import org.scalatest.DiagrammedAssertions
import sbt.util.Logger
import sbt.internal.util.ConsoleLogger

class JavaErrorParserSpec extends UnitSpec {
class JavaErrorParserSpec extends UnitSpec with DiagrammedAssertions {

"The JavaErrorParser" should "be able to parse linux errors" in parseSampleLinux()
"The JavaErrorParser" should "be able to parse Linux errors" in parseSampleLinux()
it should "be able to parse windows file names" in parseWindowsFile()
it should "be able to parse windows errors" in parseSampleWindows()
it should "be able to parse javac errors" in parseSampleJavac()
it should "register the position of errors" in parseErrorPosition()
it should "be able to parse multiple errors" in parseMultipleErrors()
it should "be able to parse multiple errors without carrets or indentation" in parseMultipleErrors2()

def parseSampleLinux() = {
val parser = new JavaErrorParser()
val logger = Logger.Null
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleLinuxMessage, logger)

problems should have size (1)
problems(0).position.sourcePath.get shouldBe ("/home/me/projects/sample/src/main/Test.java")

assert(problems.size == 1)
assert(problems(0).position.sourcePath.get == ("/home/me/projects/sample/src/main/Test.java"))
}

def parseSampleWindows() = {
val parser = new JavaErrorParser()
val logger = Logger.Null
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleWindowsMessage, logger)

problems should have size (1)
assert(problems.size == 1)
problems(0).position.sourcePath.get shouldBe (windowsFile)

}
Expand All @@ -47,35 +49,62 @@ class JavaErrorParserSpec extends UnitSpec {

def parseSampleJavac() = {
val parser = new JavaErrorParser()
val logger = Logger.Null
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleJavacMessage, logger)
problems should have size (1)
assert(problems.size == 1)
problems(0).message shouldBe (sampleJavacMessage)
}

def parseErrorPosition() = {
val parser = new JavaErrorParser()
val logger = Logger.Null
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleErrorPosition, logger)
problems should have size (1)
assert(problems.size == 1)
problems(0).position.offset.isPresent shouldBe true
problems(0).position.offset.get shouldBe 23
}

def parseMultipleErrors() = {
val parser = new JavaErrorParser()
val logger = Logger.Null
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleMultipleErrors, logger)
problems should have size (5)
assert(problems.size == 5)
}

def parseMultipleErrors2() = {
val parser = new JavaErrorParser()
val logger = ConsoleLogger()
val problems = parser.parseProblems(sampleLinuxMessage2, logger)

assert(problems.size == 3)
assert(problems(0).position.sourcePath.get == ("/home/me/projects/sample/src/main/Test.java"))
}

def sampleLinuxMessage =
"""
|/home/me/projects/sample/src/main/Test.java:4: cannot find symbol
|symbol : method baz()
|location: class Foo
|return baz();
""".stripMargin
|/home/me/projects/sample/src/main/Test.java:18: error: cannot find symbol
| baz();
| ^
| symbol: method baz()
| location: class AbstractActorRef
|1 error.
|""".stripMargin

def sampleLinuxMessage2 =
"""
|/home/me/projects/sample/src/main/Test.java:100:1: cannot find symbol
|symbol: method isBar()
|location: variable x of type com.example.List
|if (x.isBar()) {
|/home/me/projects/sample/src/main/Test.java:200:1: cannot find symbol
|symbol: method isBar()
|location: variable x of type com.example.List
|} else if (x.isBar()) {
|/home/me/projects/sample/src/main/Test.java:300:1: cannot find symbol
|symbol: method isBar()
|location: variable x of type com.example.List
|foo.baz(runtime, x.isBar());
|""".stripMargin

def sampleWindowsMessage =
s"""
Expand Down Expand Up @@ -117,5 +146,6 @@ class JavaErrorParserSpec extends UnitSpec {
|/home/foo/sbt/internal/inc/javac/test1.java:7: warning: [deprecation] RMISecurityException(String) in RMISecurityException has been deprecated
| throw new RMISecurityException("O NOES");
| ^
|5 errors.
|""".stripMargin
}

0 comments on commit 886444c

Please sign in to comment.