diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index 5b25d17b0..b4ef0d320 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -25,6 +25,7 @@ import xsbti.{ import scala.collection.JavaConverters._ import scala.collection.mutable +import java.nio.file.Path /** Provides methods for sending success and failure reports and publishing diagnostics. @@ -90,7 +91,7 @@ final class BuildServerReporterImpl( import sbt.internal.inc.JavaInterfaceUtil._ private lazy val exchange = StandardMain.exchange - private val problemsByFile = mutable.Map[VirtualFileRef, Vector[Problem]]() + private val problemsByFile = mutable.Map[Path, Vector[Problem]]() // sometimes the compiler returns a fake position such as // on Windows, this causes InvalidPathException (see #5994 and #6720) @@ -114,9 +115,10 @@ final class BuildServerReporterImpl( override def sendFailureReport(sources: Array[VirtualFile]): Unit = { for (source <- sources) { - val problems = problemsByFile.getOrElse(source, Vector.empty) + val problems = problemsByFile.getOrElse(converter.toPath(source), Vector.empty) sendReport(source, problems) } + notifyFirstReport() } private def sendReport(source: VirtualFileRef, problems: Vector[Problem]): Unit = { @@ -153,7 +155,11 @@ final class BuildServerReporterImpl( id <- problem.position.sourcePath.toOption (document, diagnostic) <- mapProblemToDiagnostic(problem) } { - val fileRef = VirtualFileRef.of(id) + // Note: We're putting the real path in `fileRef` because the `id` String can take + // two forms, either a ${something}/relativePath, or the absolute path of the source. + // But where we query this, we always have _only_ a ${something}/relativePath available. + // So here we "normalize" to the real path. + val fileRef = converter.toPath(VirtualFileRef.of(id)) problemsByFile(fileRef) = problemsByFile.getOrElse(fileRef, Vector.empty) :+ problem val params = PublishDiagnosticsParams( diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index fff085a94..d30c9f1e7 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -236,6 +236,77 @@ object BuildServerTest extends AbstractServerTest { ) } + test("buildTarget/compile [Java diagnostics] clear stale warnings") { _ => + val buildTarget = buildTargetUri("javaProj", "Compile") + val testFile = new File(svr.baseDirectory, s"java-proj/src/main/java/example/Hello.java") + + val otherBuildFile = new File(svr.baseDirectory, "force-java-out-of-process-compiler.sbt") + // Setting `javaHome` will force SBT to shell out to an external Java compiler instead + // of using the local compilation service offered by the JVM running this SBT instance. + IO.write( + otherBuildFile, + """ + |lazy val javaProj = project + | .in(file("java-proj")) + | .settings( + | javacOptions += "-Xlint:all", + | javaHome := Some(file(System.getProperty("java.home"))) + | ) + |""".stripMargin + ) + reloadWorkspace() + + compile(buildTarget) + + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":2""", + """found raw type: List""" + )(message = "should send publishDiagnostics with severity 2 for Hello.java") + + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":1""", + """incompatible types: int cannot be converted to String""" + )( + message = "should send publishDiagnostics with severity 1 for Hello.java" + ) + // Note the messages changed slightly in both cases. That's interesting… + + IO.write( + testFile, + """|package example; + | + |import java.util.List; + |import java.util.ArrayList; + | + |class Hello { + | public static void main(String[] args) { + | List list = new ArrayList<>(); + | String msg = "42"; + | System.out.println(msg); + | } + |} + |""".stripMargin + ) + + compile(buildTarget) + + assertMessage( + "build/publishDiagnostics", + "Hello.java", + "\"diagnostics\":[]", + "\"reset\":true" + )( + message = "should send publishDiagnostics with empty diagnostics" + ) + + IO.delete(otherBuildFile) + reloadWorkspace() + } + test("buildTarget/scalacOptions, buildTarget/javacOptions") { _ => val buildTargets = Seq( buildTargetUri("util", "Compile"),