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 2db8b30
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 28 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 @@ -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 2db8b30

Please sign in to comment.