Skip to content

Commit f989032

Browse files
committed
improvement: Allow passing -explain to the presentation compiler
1 parent ba45875 commit f989032

File tree

4 files changed

+109
-38
lines changed

4 files changed

+109
-38
lines changed

presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import scala.jdk.CollectionConverters.*
1515
import scala.meta.pc.VirtualFileParams
1616

1717
class DiagnosticProvider(driver: InteractiveDriver, params: VirtualFileParams):
18-
1918
def diagnostics(): List[lsp4j.Diagnostic] =
2019
if params.shouldReturnDiagnostics then
2120
val diags = driver.run(params.uri().nn, params.text().nn)
@@ -25,9 +24,13 @@ class DiagnosticProvider(driver: InteractiveDriver, params: VirtualFileParams):
2524

2625
private def toLsp(diag: Diagnostic)(using Context): Option[lsp4j.Diagnostic] =
2726
Option.when(diag.pos.exists):
27+
val message =
28+
if Diagnostic.shouldExplain(diag) then
29+
diag.msg.message + "\n\n# Explanation (enabled by `-explain`)\n\n" + diag.msg.explanation
30+
else diag.msg.message
2831
val lspDiag = lsp4j.Diagnostic(
2932
diag.pos.toLsp,
30-
diag.msg.message,
33+
message,
3134
toDiagnosticSeverity(diag.level),
3235
"presentation compiler",
3336
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dotty.tools.pc.base
2+
3+
import dotty.tools.pc.RawScalaPresentationCompiler
4+
import dotty.tools.pc.base.TestResources
5+
import dotty.tools.pc.utils.PcAssertions
6+
import dotty.tools.pc.utils.TestExtensions.getOffset
7+
import org.eclipse.lsp4j.Diagnostic
8+
import org.eclipse.lsp4j.DiagnosticSeverity
9+
10+
import java.net.URI
11+
import scala.meta.internal.jdk.CollectionConverters.*
12+
import scala.meta.internal.metals.EmptyCancelToken
13+
import scala.meta.pc.CancelToken
14+
import scala.meta.pc.VirtualFileParams
15+
16+
class BaseDiagnosticsSuite extends PcAssertions:
17+
case class TestDiagnostic(startIndex: Int, endIndex: Int, msg: String, severity: DiagnosticSeverity)
18+
19+
def options : List[String] = Nil
20+
21+
val pc = RawScalaPresentationCompiler().newInstance("", TestResources.classpath.asJava, options.asJava)
22+
23+
case class TestVirtualFileParams(uri: URI, text: String) extends VirtualFileParams {
24+
override def shouldReturnDiagnostics: Boolean = true
25+
override def token: CancelToken = EmptyCancelToken
26+
}
27+
28+
def check(
29+
text: String,
30+
expected: List[TestDiagnostic],
31+
additionalChecks: List[Diagnostic] => Unit = identity
32+
): Unit =
33+
val diagnostics = pc
34+
.didChange(TestVirtualFileParams(URI.create("file:/Diagnostic.scala"), text))
35+
.asScala
36+
37+
val actual = diagnostics.map(d => TestDiagnostic(d.getRange().getStart().getOffset(text), d.getRange().getEnd().getOffset(text), d.getMessage(), d.getSeverity()))
38+
assertEquals(expected, actual, s"Expected [${expected.mkString(", ")}] but got [${actual.mkString(", ")}]")
39+
additionalChecks(diagnostics.toList)

presentation-compiler/test/dotty/tools/pc/tests/DiagnosticProviderSuite.scala

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,23 @@
11
package dotty.tools.pc.tests
22

3-
import java.net.URI
4-
import scala.meta.internal.jdk.CollectionConverters.*
5-
import scala.meta.internal.metals.EmptyCancelToken
6-
import scala.meta.pc.VirtualFileParams
7-
import scala.meta.pc.CancelToken
8-
9-
import org.junit.Test
10-
import org.eclipse.lsp4j.DiagnosticSeverity
11-
import dotty.tools.pc.utils.TestExtensions.getOffset
12-
import dotty.tools.pc.base.TestResources
13-
import java.nio.file.Path
14-
import dotty.tools.pc.RawScalaPresentationCompiler
15-
import dotty.tools.pc.utils.PcAssertions
16-
import org.eclipse.lsp4j.Diagnostic
3+
import dotty.tools.pc.base.BaseDiagnosticsSuite
174
import org.eclipse.lsp4j.CodeAction
5+
import org.eclipse.lsp4j.Diagnostic
6+
import org.eclipse.lsp4j.DiagnosticSeverity
7+
import org.junit.Test
188

19-
class DiagnosticProviderSuite extends PcAssertions {
20-
case class TestDiagnostic(startIndex: Int, endIndex: Int, msg: String, severity: DiagnosticSeverity)
21-
22-
val pc = RawScalaPresentationCompiler().newInstance("", TestResources.classpath.asJava, Nil.asJava)
23-
24-
case class TestVirtualFileParams(uri: URI, text: String) extends VirtualFileParams {
25-
override def shouldReturnDiagnostics: Boolean = true
26-
override def token: CancelToken = EmptyCancelToken
27-
}
28-
29-
def check(
30-
text: String,
31-
expected: List[TestDiagnostic],
32-
additionalChecks: List[Diagnostic] => Unit = identity
33-
): Unit =
34-
val diagnostics = pc
35-
.didChange(TestVirtualFileParams(URI.create("file:/Diagnostic.scala"), text))
36-
.asScala
9+
import java.net.URI
10+
import scala.meta.internal.jdk.CollectionConverters.*
3711

38-
val actual = diagnostics.map(d => TestDiagnostic(d.getRange().getStart().getOffset(text), d.getRange().getEnd().getOffset(text), d.getMessage(), d.getSeverity()))
39-
assertEquals(expected, actual, s"Expected [${expected.mkString(", ")}] but got [${actual.mkString(", ")}]")
40-
additionalChecks(diagnostics.toList)
12+
class DiagnosticProviderSuite extends BaseDiagnosticsSuite {
4113

4214
@Test def error =
4315
check(
4416
"""|object M:
4517
| Int.maaxValue
4618
|""".stripMargin,
4719
List(TestDiagnostic(12,25, "value maaxValue is not a member of object Int - did you mean Int.MaxValue?", DiagnosticSeverity.Error))
48-
)
20+
)
4921

5022
@Test def warning =
5123
check(
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package dotty.tools.pc.tests
2+
3+
import dotty.tools.pc.base.BaseDiagnosticsSuite
4+
import org.eclipse.lsp4j.CodeAction
5+
import org.eclipse.lsp4j.Diagnostic
6+
import org.eclipse.lsp4j.DiagnosticSeverity
7+
import org.junit.Test
8+
9+
import java.net.URI
10+
import scala.meta.internal.jdk.CollectionConverters.*
11+
12+
class ExplainDiagnosticProviderSuite extends BaseDiagnosticsSuite {
13+
14+
override def options : List[String] = List("-explain")
15+
16+
@Test def error1 =
17+
check(
18+
"""|object C:
19+
| def m(x: Int) = 1
20+
| object T extends K:
21+
| val x = m(1) // error
22+
|class K:
23+
| def m(i: Int) = 2
24+
|""".stripMargin,
25+
List(
26+
TestDiagnostic(
27+
64,65,
28+
"""|Reference to m is ambiguous.
29+
|It is both defined in object C
30+
|and inherited subsequently in object T
31+
|
32+
|# Explanation (enabled by `-explain`)
33+
|
34+
|The identifier m is ambiguous because a name binding of lower precedence
35+
|in an inner scope cannot shadow a binding with higher precedence in
36+
|an outer scope.
37+
|
38+
|The precedence of the different kinds of name bindings, from highest to lowest, is:
39+
| - Definitions in an enclosing scope
40+
| - Inherited definitions and top-level definitions in packages
41+
| - Names introduced by import of a specific name
42+
| - Names introduced by wildcard import
43+
| - Definitions from packages in other files
44+
|Note:
45+
| - As a rule, definitions take precedence over imports.
46+
| - Definitions in an enclosing scope take precedence over inherited definitions,
47+
| which can result in ambiguities in nested classes.
48+
| - When importing, you can avoid naming conflicts by renaming:
49+
| import scala.{m => mTick}
50+
|""".stripMargin,
51+
DiagnosticSeverity.Error
52+
53+
)
54+
)
55+
)
56+
57+
}

0 commit comments

Comments
 (0)