mirror of https://github.com/sbt/sbt.git
[2.0.x] Fix publishDiagnostics propagation (#9376)
**Problem** Test is based on https://github.com/sbt/sbt/issues/9345#issuecomment-4718229113 which gives us the following sequence: 1. Metals sends buildTarget/compile. 2. sbt publishes real non-empty diagnostics. 3. Metals sends buildTarget/scalaMainClasses. 4. During that request, sbt emits build/publishDiagnostics with diagnostics: [] and reset: true. 5. The following build/taskFinish still reports errors: 1. Previously, errors for diagnostics reporting via bsp were collected from a live compilation run. In the sequence above, that is triggered by buildTarget/compile. Then, buildTarget/scalaMainClasses does not trigger such a run for the second time, it uses the cached compilation result. Therefore, the diagnostics is not populated. **Solution** The proposed fix modifies sendFailureReport to accept an optional CompileFailed object that contains the diagnostics even in case the actual compiler did not run because the cached result was used. If no problems were found for a file via default means, this CompileFailed object is queried to see if it has any information about problems in a given file.
This commit is contained in:
parent
e3ceea5722
commit
36aa899474
|
|
@ -780,6 +780,10 @@ lazy val mainProj = (project in file("main"))
|
|||
exclude[DirectMissingMethodProblem]("sbt.internal.ClassStamper.stampVf"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.internal.CompileInputs2.*"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.internal.IncrementalTest.cacheInput"),
|
||||
// Added optional CompileFailed context for BSP failure diagnostics (sbt#9345)
|
||||
exclude[ReversedMissingMethodProblem](
|
||||
"sbt.internal.server.BuildServerReporter.sendFailureReport"
|
||||
),
|
||||
),
|
||||
)
|
||||
.dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing)
|
||||
|
|
|
|||
|
|
@ -2278,7 +2278,7 @@ object Defaults extends BuildCommon {
|
|||
res
|
||||
case Result.Inc(cause) =>
|
||||
val compileFailed = cause.directCause.collect { case c: CompileFailed => c }
|
||||
reporter.sendFailureReport(ci.options.sources)
|
||||
reporter.sendFailureReport(ci.options.sources, compileFailed)
|
||||
bspTask.notifyFailure(compileFailed)
|
||||
throw cause
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import sbt.internal.util.ManagedLogger
|
|||
import sbt.internal.server.BuildServerProtocol.BspCompileState
|
||||
import xsbti.compile.CompileAnalysis
|
||||
import xsbti.{
|
||||
CompileFailed,
|
||||
FileConverter,
|
||||
Problem,
|
||||
Reporter,
|
||||
|
|
@ -24,6 +25,7 @@ import xsbti.{
|
|||
}
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
import scala.jdk.OptionConverters.*
|
||||
import scala.collection.mutable
|
||||
import java.nio.file.Path
|
||||
|
||||
|
|
@ -46,6 +48,11 @@ sealed trait BuildServerReporter extends Reporter {
|
|||
|
||||
def sendFailureReport(sources: Array[VirtualFile]): Unit
|
||||
|
||||
def sendFailureReport(
|
||||
sources: Array[VirtualFile],
|
||||
failure: Option[CompileFailed]
|
||||
): Unit
|
||||
|
||||
override def reset(): Unit = underlying.reset()
|
||||
|
||||
override def hasErrors: Boolean = underlying.hasErrors
|
||||
|
|
@ -113,9 +120,30 @@ final class BuildServerReporterImpl(
|
|||
notifyFirstReport()
|
||||
}
|
||||
|
||||
override def sendFailureReport(sources: Array[VirtualFile]): Unit = {
|
||||
override def sendFailureReport(sources: Array[VirtualFile]): Unit =
|
||||
sendFailureReport(sources, None)
|
||||
|
||||
override def sendFailureReport(
|
||||
sources: Array[VirtualFile],
|
||||
failure: Option[CompileFailed]
|
||||
): Unit = {
|
||||
val fallbackByFile: Map[Path, Vector[Problem]] = failure match
|
||||
case Some(failed) =>
|
||||
failed
|
||||
.problems()
|
||||
.toVector
|
||||
.flatMap { problem =>
|
||||
problem.position.sourcePath.toScala.map { id =>
|
||||
converter.toPath(VirtualFileRef.of(id)) -> problem
|
||||
}
|
||||
}
|
||||
.groupMap(_._1)(_._2)
|
||||
case None =>
|
||||
Map.empty
|
||||
|
||||
for (source <- sources) {
|
||||
val problems = problemsByFile.getOrElse(converter.toPath(source), Vector.empty)
|
||||
val path = converter.toPath(source)
|
||||
val problems = problemsByFile.getOrElse(path, fallbackByFile.getOrElse(path, Vector.empty))
|
||||
sendReport(source, problems)
|
||||
}
|
||||
notifyFirstReport()
|
||||
|
|
@ -245,7 +273,13 @@ final class BuildServerForwarder(
|
|||
analysis: CompileAnalysis,
|
||||
): Unit = ()
|
||||
|
||||
override def sendFailureReport(sources: Array[VirtualFile]): Unit = ()
|
||||
override def sendFailureReport(sources: Array[VirtualFile]): Unit =
|
||||
sendFailureReport(sources, None)
|
||||
|
||||
override def sendFailureReport(
|
||||
sources: Array[VirtualFile],
|
||||
failure: Option[CompileFailed]
|
||||
): Unit = ()
|
||||
|
||||
protected override def publishDiagnostic(problem: Problem): Unit = ()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@
|
|||
package testpkg
|
||||
|
||||
import sbt.internal.bsp.*
|
||||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.IO
|
||||
// import sbt.internal.bsp.codec.JsonProtocol.given
|
||||
import sbt.internal.langserver.ErrorCodes // , LogMessageParams }
|
||||
// import sbt.internal.langserver.codec.JsonProtocol.given
|
||||
// import sbt.internal.protocol.JsonRpcNotificationMessage
|
||||
import sbt.internal.protocol.JsonRpcRequestMessage
|
||||
import sbt.internal.protocol.codec.JsonRPCProtocol.given
|
||||
import sbt.IO
|
||||
import sjsonnew.JsonWriter
|
||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||
|
||||
|
|
@ -19,6 +22,7 @@ import java.io.File
|
|||
import java.net.URI
|
||||
import java.nio.file.{ Files, Paths }
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
// import java.util.concurrent.TimeoutException
|
||||
import scala.concurrent.duration.*
|
||||
|
||||
// starts svr using server-test/buildserver and perform custom server tests
|
||||
|
|
|
|||
Loading…
Reference in New Issue