Skip to content

Commit 5af9985

Browse files
BSP diagnostics fixes / tweaking (#5349)
This adds tests related to diagnostics in BSP integration tests, and fixes or adjusts several things related to diagnostics in BSP. In particular, this ensures that a `CompileProblemReporter` is always passed around when running tasks for BSP. This ensures that users get all diagnostics related to compilation happening in BSP requests, not only diagnostics happening in the `buildTarget/compile` request. This includes compilation of the build itself, that happens upfront right after having started the BSP server. This allows users to get diagnostics for errors that make the build not compile for example (which can help address those errors). This fixes things related to generated files. Some diagnostics notifications were not sent for those, which was making diagnostics stick around in VSCode, even when the errors were addressed. Line adjustment was incorrect for `build.mill` files having no `package` directive. This fixes that too. There was also a hardcoded limit of maximum 10 errors being reported (both in BSP and in the console). This removes this limit for BSP, where a large amount of errors shouldn't be a problem, given they're reported inline in sources. Lastly, the "Don't send diagnostics twice" commit ensures we don't send duplicate diagnostics via BSP. Ensuring a CompileProblemReporter is passed around everywhere (see above) can sometimes trigger that (when semanticdb generation is enabled, compilation happens twice: once for classes, a second time for semanticdbs, which would report each diagnostic twice).
1 parent 517bb77 commit 5af9985

37 files changed

+1271
-97
lines changed

core/api/src/mill/api/internal/CompileProblemReporter.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ trait CompileProblemReporter {
1515
def printSummary(): Unit
1616
def finish(): Unit
1717
def notifyProgress(percentage: Long, total: Long): Unit
18+
19+
def maxErrors: Int = CompileProblemReporter.defaultMaxErrors
20+
}
21+
22+
object CompileProblemReporter {
23+
def defaultMaxErrors: Int = 10
1824
}
1925

2026
/**

core/api/src/mill/api/internal/EvaluatorApi.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ trait EvaluatorApi extends AutoCloseable {
77
def evaluate(
88
scriptArgs: Seq[String],
99
selectMode: SelectMode,
10+
reporter: Int => Option[CompileProblemReporter] = _ => None,
1011
selectiveExecution: Boolean = false
1112
): Result[EvaluatorApi.Result[Any]]
1213

core/define/src/mill/define/Evaluator.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ trait Evaluator extends AutoCloseable with EvaluatorApi {
8181
def evaluate(
8282
scriptArgs: Seq[String],
8383
selectMode: SelectMode = SelectMode.Separated,
84+
reporter: Int => Option[CompileProblemReporter] = _ => None,
8485
selectiveExecution: Boolean = false
8586
): mill.api.Result[Evaluator.Result[Any]]
8687

core/define/src/mill/define/EvaluatorProxy.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,10 @@ final class EvaluatorProxy(var delegate0: () => Evaluator) extends Evaluator {
9898
def evaluate(
9999
scriptArgs: Seq[String],
100100
selectMode: SelectMode,
101+
reporter: Int => Option[CompileProblemReporter] = _ => None,
101102
selectiveExecution: Boolean = false
102103
): mill.api.Result[Evaluator.Result[Any]] = {
103-
delegate.evaluate(scriptArgs, selectMode, selectiveExecution)
104+
delegate.evaluate(scriptArgs, selectMode, reporter, selectiveExecution)
104105
}
105106
def close = delegate0 = null
106107

core/eval/src/mill/eval/EvaluatorImpl.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ final class EvaluatorImpl private[mill] (
247247
def evaluate(
248248
scriptArgs: Seq[String],
249249
selectMode: SelectMode,
250+
reporter: Int => Option[CompileProblemReporter] = _ => None,
250251
selectiveExecution: Boolean = false
251252
): mill.api.Result[Evaluator.Result[Any]] = {
252253
val resolved = os.checker.withValue(ResolveChecker(workspace)) {
@@ -261,7 +262,7 @@ final class EvaluatorImpl private[mill] (
261262
}
262263

263264
for (targets <- resolved)
264-
yield execute(Seq.from(targets), selectiveExecution = selectiveExecution)
265+
yield execute(Seq.from(targets), reporter = reporter, selectiveExecution = selectiveExecution)
265266
}
266267

267268
def close(): Unit = execution.close()

integration/bsp-util/src/BspServerTestUtil.scala

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,49 +64,39 @@ object BspServerTestUtil {
6464
// This can be false only when generating test data for the first time.
6565
// In that case, updateSnapshots needs to be true, so that we write test data on disk.
6666
val snapshotExists = os.exists(snapshotPath)
67-
val expectedValueOpt = Option.when(snapshotExists) {
68-
gson.fromJson(
69-
normalizeLocalValues(os.read(snapshotPath), inverse = true),
70-
implicitly[ClassTag[T]].runtimeClass
71-
)
72-
}
7367

74-
lazy val jsonStr = normalizeLocalValues(
68+
val jsonStr = normalizeLocalValues(
7569
gson.toJson(
7670
value,
7771
implicitly[ClassTag[T]].runtimeClass
7872
)
7973
)
8074

81-
lazy val expectedJsonStr = expectedValueOpt match {
82-
case Some(v) => normalizeLocalValues(gson.toJson(v, implicitly[ClassTag[T]].runtimeClass))
83-
case None => ""
84-
}
8575
if (updateSnapshots) {
8676
System.err.println(if (snapshotExists) s"Updating $snapshotPath"
8777
else s"Writing $snapshotPath")
8878
os.write.over(snapshotPath, jsonStr, createFolders = true)
89-
} else {
90-
Predef.assert(
91-
jsonStr == expectedJsonStr,
92-
if (snapshotExists) {
93-
val diff = os.call((
94-
// "git",
95-
"diff",
96-
"-u",
97-
os.temp(expectedJsonStr, suffix = s"${snapshotPath.last}-expectedJsonStr"),
98-
os.temp(jsonStr, suffix = s"${snapshotPath.last}-jsonStr")
99-
))
79+
} else if (snapshotExists) {
80+
val expectedJsonStr = os.read(snapshotPath)
81+
if (jsonStr != expectedJsonStr) {
82+
val diff = os.call((
83+
"diff",
84+
"-u",
85+
os.temp(expectedJsonStr, suffix = s"${snapshotPath.last}-expectedJsonStr"),
86+
os.temp(jsonStr, suffix = s"${snapshotPath.last}-jsonStr")
87+
))
88+
sys.error(
10089
s"""Error: value differs from snapshot in $snapshotPath
10190
|
10291
|You might want to set BspServerTestUtil.updateSnapshots to true,
10392
|run this test again, and commit the updated test data files.
10493
|
10594
|$diff
10695
|""".stripMargin
107-
} else s"Error: no snapshot found at $snapshotPath"
108-
)
109-
}
96+
)
97+
}
98+
} else
99+
sys.error(s"Error: no snapshot found at $snapshotPath")
110100
}
111101

112102
def compareLogWithSnapshot(

integration/ide/bsp-server/resources/project/build.mill

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,18 @@ object delayed extends scalalib.ScalaModule {
9191
res
9292
}
9393
}
94+
95+
object diag extends scalalib.ScalaModule {
96+
def scalaVersion = Option(System.getenv("TEST_SCALA_2_13_VERSION")).getOrElse(???)
97+
def scalacOptions = Seq("-deprecation")
98+
99+
@deprecated("deprecated", "0.0.1")
100+
def thing = 2
101+
102+
def theThing = thing
103+
104+
object many extends scalalib.ScalaModule {
105+
def scalaVersion = Option(System.getenv("TEST_SCALA_2_13_VERSION")).getOrElse(???)
106+
def scalacOptions = Seq("-deprecation")
107+
}
108+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package diag
2+
3+
object WarningAndErrors {
4+
5+
@deprecated("thing", "0.0.1")
6+
def printThing() = println(2)
7+
8+
// 12 warnings for "printThing()" calls
9+
// 12 errors for not found value "foo"
10+
11+
def thing0 = {
12+
printThing()
13+
foo
14+
}
15+
16+
def thing1 = {
17+
printThing()
18+
foo
19+
}
20+
21+
def thing2 = {
22+
printThing()
23+
foo
24+
}
25+
26+
def thing3 = {
27+
printThing()
28+
foo
29+
}
30+
31+
def thing4 = {
32+
printThing()
33+
foo
34+
}
35+
36+
def thing5 = {
37+
printThing()
38+
foo
39+
}
40+
41+
def thing6 = {
42+
printThing()
43+
foo
44+
}
45+
46+
def thing7 = {
47+
printThing()
48+
foo
49+
}
50+
51+
def thing8 = {
52+
printThing()
53+
foo
54+
}
55+
56+
def thing9 = {
57+
printThing()
58+
foo
59+
}
60+
61+
def thing10 = {
62+
printThing()
63+
foo
64+
}
65+
66+
def thing11 = {
67+
printThing()
68+
foo
69+
}
70+
71+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package check
2+
3+
object DiagCheck {
4+
5+
@deprecated("deprecated thing", "0.0.1")
6+
def thing = 2
7+
8+
println(thing)
9+
10+
}

integration/ide/bsp-server/resources/snapshots/build-targets-compile-classpaths.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@
3535
"file:///workspace/delayed/compile-resources"
3636
]
3737
},
38+
{
39+
"target": {
40+
"uri": "file:///workspace/diag"
41+
},
42+
"classpath": [
43+
"file:///coursier-cache/https/repo1.maven.org/maven2/org/scala-lang/scala-library/<scala-version>/scala-library-<scala-version>.jar",
44+
"file:///workspace/diag/compile-resources"
45+
]
46+
},
47+
{
48+
"target": {
49+
"uri": "file:///workspace/diag/many"
50+
},
51+
"classpath": [
52+
"file:///coursier-cache/https/repo1.maven.org/maven2/org/scala-lang/scala-library/<scala-version>/scala-library-<scala-version>.jar",
53+
"file:///workspace/diag/many/compile-resources"
54+
]
55+
},
3856
{
3957
"target": {
4058
"uri": "file:///workspace/errored/compilation-error"

0 commit comments

Comments
 (0)