Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement quickfixes, aka actionable diagnostics (via CodeAction) #10406

Merged
merged 1 commit into from
Jul 6, 2023

Conversation

eed3si9n
Copy link
Member

@eed3si9n eed3si9n commented May 20, 2023

Ref https://contributors.scala-lang.org/t/roadmap-for-actionable-diagnostics/6172
Fixes scala/scala-dev#844

This implements new reporter methods that take code actions as a parameter.
The purpose is to pass these along to Zinc and BSP so that the compiler can suggest code edits to the editors in a structural manner.

To show case this feature, I've added CodeActions to the following deprecations:

  • Deprecation of procedure syntax
  • Deprecation of empty-paren method auto-application
  • Deprecation of val in for-comprehension
  • Deprecation of paren-less lambda parameter

Note

Here's the flow of information

┌──────┐doReport┌────────┐
│scalac├───────►│compiler│
└──────┘        │bridge  │
                └──┬─────┘
                   │  xsbti.Problem
┌──────────┐     ┌─▼──┐
│BSP server│◄────┤Zinc│
└──┬───────┘     └────┘
   │  Diagnostic
┌──▼───────────────┐
│BSP client        │
│(Metals, IntelliJ)│
└──┬───────────────┘
   │  Diagnostic
┌──▼───────┐
│LSP Client│
└──────────┘

@lrytz
Copy link
Member

lrytz commented May 24, 2023

This is looking very good, thank you!

I wonder if we can already push a bit harder to the new API, for example,

  • make info0 @deprecatedInheritance
  • use a default parameter in the internal classes (e.g., in PerRunReporting, FilteringReporter.doReport)
  • Change StoreReporter.Info to add an actions field

Why do we need CodeAction in scala.reflect.api?

I guess the boilerplate in CodeAction is about the possibility for binary compatible evolution? Do you have a situation in mind where this could be useful?

@eed3si9n
Copy link
Member Author

Why do we need CodeAction in scala.reflect.api?

Initially I thought CodeAction might leak out like position would, but now that you mention it, I can probably delete the scala.reflect.api traits.

I guess the boilerplate in CodeAction is about the possibility for binary compatible evolution? Do you have a situation in mind where this could be useful?

This was mostly copied from similar code I added in sbt, but I guess I can use normal case classes within scalac.

I wonder if we can already push a bit harder to the new API, for example, ...

I'll look into those.

@lrytz
Copy link
Member

lrytz commented May 24, 2023

We're more than happy to help out, let me know.

@eed3si9n
Copy link
Member Author

We're more than happy to help out, let me know.

Feel free to add commits on top (or a new PR). I am mostly interested in getting code actions out, and I trust your judgement on how implementation can be improved. I'll comment here before working on something to avoid duplication.

@eed3si9n
Copy link
Member Author

I'll start with the removal of api traits, and converting to case classes.

@eed3si9n
Copy link
Member Author

ok. Made a few more changes per suggestion.

@som-snytt
Copy link
Contributor

I can't wait for -Xreporter RewriteReporter perhaps enabled under -rewrite.

@lrytz lrytz force-pushed the wip/reporter branch 2 times, most recently from 44e61dd to 1d11931 Compare May 31, 2023 14:23
@lrytz
Copy link
Member

lrytz commented May 31, 2023

I think this should be about it for now, a List[CodeAction] paramter is available for all reporting methods in the compiler except

  • Typer errors, these go through some abstractions: methods in ContextErrors, which build AbsTypeError subclasses. The actions field can be added to these subclasses as needed, they are internal anyway. But the core is there (AbsTypeError has an accessor for actions).
  • Scanner errors / warnings, it seemed there are no cases there where we could provide a quick fix. Scanner deprecations have an actions parameter (unicode ).

I did a basic sbt test by publishLocal and running sbt -Dstarr.version=....

@lrytz
Copy link
Member

lrytz commented May 31, 2023

Now we should figure out if it breaks any tools or macros. Ping @adpi2 @tgodzik @ckipp01 @smarter @sjrd @SethTisue for some help coming up with projects to test. Maybe testing is easier after merging the PR when we have an intergration build.

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be good in the projects that we use the compiler directly. Though to be 100% sure I would need to try and compile the affected projects.

@@ -97,11 +93,16 @@ object Reporter {
abstract class FilteringReporter extends Reporter {
def settings: Settings

@deprecatedOverriding("override the `doReport` overload instead", "2.13.12")
@deprecated("use the `doReport` overload instead", "2.13.12")
def doReport(pos: Position, msg: String, severity: Severity): Unit = doReport(pos, msg, severity, Nil)
Copy link
Contributor

@tgodzik tgodzik May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this is kept, so we should be asafe, I think doReport is used in scalameta/scalameta and scalameta/mdoc. Possibly also scala-cli?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the method is source compatible, but extending FilteringReporter is not. The abstract method used to be

  def doReport(pos: Position, msg: String, severity: Severity): Unit

and now it's

  @deprecatedOverriding("override the `doReport` overload instead", "2.13.12")
  @deprecated("use the `doReport` overload instead", "2.13.12")
  def doReport(pos: Position, msg: String, severity: Severity): Unit = doReport(pos, msg, severity, Nil)

  override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will be a problem for mdoc then since we don't publish for each Scala version :/ This of course causes us problems, but on the other hand we can support all the Scala versions without separate publishing

Any idea how we should work around that? I can take a look at it after Scala Days, but I am bit terrified as I don't fully remember why we wrote it this way. I guess some of the classes were not available in the older versions and I had to work around it this way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so ideally an mdoc release should be binary compatible with all Scala 2.13 versions, i.e.,

  • existing ones should continue to work with new Scala releases, and
  • new mdoc releases built with a new Scala version should work with older Scala versions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the idea, but it's based on an assumption that the Report API wouldn't change too much 😓 which is clearly a wrong assumption.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a small change that should restore binary compatibility (both ways) with existing FilteringReporter subclasses. I tested it with the following:

import scala.tools.nsc.Settings
import scala.reflect.internal.util.Position
// import scala.reflect.internal.util.CodeAction
import scala.reflect.internal.Reporter.Severity
import scala.annotation.nowarn
import scala.tools.nsc.reporters.FilteringReporter

trait MyRepT extends FilteringReporter { self: MyRep =>
  @nowarn("cat=deprecation")
  override def doReport(pos: Position, msg: String, severity: Severity): Unit =
    add(pos, msg, severity)

  // override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
    // add(pos, msg, severity)
}

class MyRep(val settings: Settings) extends MyRepT {
  def add(pos: Position, msg: String, severity: Severity): Unit =
    println(s"[myrep] $msg")
}

Compiling this class with 2.13.10, I can use the reporter with both 2.13.10 and this PR

➜ sandbox java -cp $(cs fetch -p org.scala-lang:scala-compiler:2.13.10):. scala.tools.nsc.Main -usejavacp -Xreporter:MyRep A.scala
[myrep] type mismatch;
 found   : String("")
 required: Int

➜ sandbox qsc -toolcp . -Xreporter:MyRep A.scala
[myrep] type mismatch;
 found   : String("")
 required: Int

I also compiled the class with this PR's compiler and it works with 2.13.10, with or without un-commenting the doReport overload.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think that should work for us! I can confirm 100% when there is a nightly version released

@lrytz
Copy link
Member

lrytz commented Jun 16, 2023

This is getting ready I think.

@ckipp01 could you take a look at CodeAction.scala in this PR to see if it looks right to you?

One idea: instead of the actions: List[CodeAction] parameter, we could use data: Map[String, Any] and put the List[CodeAction] in there as Map("codeActions" -> List(CodeAction(...))). @eed3si9n wdyt?

@ckipp01
Copy link
Member

ckipp01 commented Jun 16, 2023

This is getting ready I think.

@ckipp01 could you take a look at CodeAction.scala in this PR to see if it looks right to you?

Yup, just took a look and everything looks good! I did try to get it to work with the changes I made in Metals here, but was having a lot of issues getting it to work since I kept getting issues with Metals not being able to get semanticdb for the self-published version of Scala, even though I kept trying to turn of semanticDB in sbt. Either way, the shape that you have oulined in here seems to match what @eed3si9n made in BSP here and what I am using in Dotty here, so we should be good.

Yup, this is fine. The code action that we made here differs a bit from the LSP one. I added in the description here just in case there was something we could use it for in the future. For now I don't really know what it would be used for, but I could imagine that in a UI somewhere it could be helpful. Basically we take this and translate it into an LSP code action in Metals and other clients using LSP, and then IntelliJ can also just take this and translate it into their "action" format.

  • is it fine to use the compiler's Position in TextEdit?

Yup, also fine. These Positions will get turned into a Range at least for us in the LSP world. We do the same with the Diagnostic going from a Position to Range.

One idea: instead of the actions: List[CodeAction] parameter, we could use data: Map[String, Any] and put the List[CodeAction] in there as Map("codeActions" -> List(CodeAction(...))). @eed3si9n wdyt?

Actually it started out as a Map originally but @eed3si9n had a comment here about it:

We should avoid Map since this could introduce non-determinism when persisted into JSON, and Problem is a good candidate for caching.

@lrytz
Copy link
Member

lrytz commented Jun 16, 2023

Thanks for taking a look!

I don't mean to put the Map in the CodeAction structure.

My proposal is to use a Map in the Scala 2 compiler's Reporter methods. This would allow passing other data from the compiler to downstream tools in the future without such a big refactoring as this PR.

The compiler interface (which lives in the zinc repo for Scala 2, not in the compiler repo) would pull out the CodeActions and translate them into the zinc Problem representation (IIRC).

@eed3si9n
Copy link
Member Author

To clarify this discussion I made a diagram:

┌──────┐doReport┌────────┐
│scalac├───────►│compiler│
└──────┘        │bridge  │
                └──┬─────┘
                   │  xsbti.Problem
┌──────────┐     ┌─▼──┐
│BSP server│◄────┤Zinc│
└──┬───────┘     └────┘
   │  Diagnostic
┌──▼───────────────┐
│BSP client        │
│(Metals, IntelliJ)│
└──┬───────────────┘
   │  Diagnostic
┌──▼───────┐
│LSP Client│
└──────────┘

@SethTisue SethTisue added prio:hi high priority (used only by core team, only near release time) release-notes worth highlighting in next release notes labels Jun 27, 2023
@lrytz
Copy link
Member

lrytz commented Jul 6, 2023

I think this is ready now. @eed3si9n would you take another look?

There is still the idea to change the actions parameter from

  • actions: List[CodeAction] to
  • data: Map[String, Any] with Map("codeActions" -> List(CodeAction(...)))
    but I have any concrete use case for it in mind, so it's probably better to not over-generalize.

The next step will be to adapt the compiler bridge. @eed3si9n as far as I can tell, bridge sources in zinc are by compiler major version. The current bridge sources work with the 2.13 compiler after this PR, but with the current model we'd also need to make sure the adapted bridge that forwards code actions works with older compilers. Do you already have ideas how this could be adressed?

@eed3si9n
Copy link
Member Author

eed3si9n commented Jul 6, 2023

as far as I can tell, bridge sources in zinc are by compiler major version. The current bridge sources work with the 2.13 compiler after this PR, but with the current model we'd also need to make sure the adapted bridge that forwards code actions works with older compilers. Do you already have ideas how this could be addressed?

We could potentially:

  • split the compiler bridge for 2.13.12 onwards, or
  • keep the existing bridge for 2.13.x, but just ship an override DelegatingReporter.scala in Zinc itself as resource, or
  • keep the existing bridge for 2.13.x, and detect 2.13.12+ in the code and split the call to structural type (reflection per compiler warning, but hopefully not too bad)

@eed3si9n
Copy link
Member Author

eed3si9n commented Jul 6, 2023

would you take another look?

LGTM as far as the PR goes. Like tgodzik said in the comment above, we'd find out if all the pieces snap once nightly comes out.

Add a new `actions: List[CodeAction]` parameter to reporting methods.
The purpose is to pass these along to Zinc and BSP so that the
compiler can suggest code edits to the editors in a structural manner.

To showcase this feature, CodeActions are added for
* Deprecation of procedure syntax
* Deprecation of empty-paren method auto-application
* Deprecation of val in for-comprehension
* Deprecation of paren-less lambda parameter

Existing subclasses of FilteringReporter should continue to work,
though the compiler is generally not binary compatible between minor
versions.
@lrytz
Copy link
Member

lrytz commented Jul 6, 2023

  • split the compiler bridge for 2.13.12 onwards

I think I'd prefer that, it needs some work to set up. Would the dispatching be done in AnalyzingCompiler.scala?

How are the bridge sources distributed?

We could still consider moving the bridge into scala/scala, but that's beyond scope right now.

  • keep the existing bridge for 2.13.x, but just ship an override DelegatingReporter.scala in Zinc itself as resource, or

If that's easier - I'd need your help

  • keep the existing bridge for 2.13.x, and detect 2.13.12+ in the code and split the call to structural type (reflection per compiler warning, but hopefully not too bad)

If we're using MethodHandle the right way (instead of old school reflection), performance should be the same as a compiled method call. Then the changes would be local to the bridge source code. But development / maintenance is more difficult as we don't get help from the compiler about getting things right.

@lrytz lrytz merged commit 286f8d7 into scala:2.13.x Jul 6, 2023
3 checks passed
@eed3si9n eed3si9n deleted the wip/reporter branch July 6, 2023 20:43
@eed3si9n
Copy link
Member Author

eed3si9n commented Jul 7, 2023

  • split the compiler bridge for 2.13.12 onwards

I think I'd prefer that, it needs some work to set up. Would the dispatching be done in AnalyzingCompiler.scala?

How are the bridge sources distributed?

compiler bridge is distributed like a normal artifact on Maven Central (https://repo1.maven.org/maven2/org/scala-sbt/compiler-bridge_2.13/1.9.2/), the downside of approach is that the dispatch is done at the build tool level - https://github.com/sbt/sbt/blob/4b0b929838c7525cbacd790c2111997d80a5c72e/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincLmUtil.scala#L85-L100. I wouldn't be surprised other build tools assume naming convention.

We could still consider moving the bridge into scala/scala, but that's beyond scope right now.

  • keep the existing bridge for 2.13.x, but just ship an override DelegatingReporter.scala in Zinc itself as resource, or

If that's easier - I'd need your help

  • keep the existing bridge for 2.13.x, and detect 2.13.12+ in the code and split the call to structural type (reflection per compiler warning, but hopefully not too bad)

If we're using MethodHandle the right way (instead of old school reflection), performance should be the same as a compiled method call. Then the changes would be local to the bridge source code. But development / maintenance is more difficult as we don't get help from the compiler about getting things right.

Yea, either approach works. The compiler bridge is pretty well exercised, so between two options I guess having a single DelegatingReporter.scala is slightly more preferable.

Given that we need to override a method that takes a class non-existent in 2.13.11, supplying a whole DelegatingReporter.scala might be easier actually.

@lrytz
Copy link
Member

lrytz commented Jul 7, 2023

Moved the discussion here: sbt/zinc#1214

@SethTisue SethTisue removed the prio:hi high priority (used only by core team, only near release time) label Jul 7, 2023
@SethTisue
Copy link
Member

in the community build, over at scala/community-build#1685 , this broke scalameta:

[scalameta] [error] /Users/tisue/cb3/target-0.9.20/project-builds/scalameta-bfcdfa42ad4bddde4b08583b10292faad66d430a/scalameta/testkit/jvm/src/test/scala/scala/meta/testkit/ScalacParser.scala:51:20: method incompleteInputError overrides nothing.
[scalameta] [error] Note: the super classes of <$anon: scala.meta.testkit.ScalacParser.global.syntaxAnalyzer.UnitParser> contain the following, non final members named incompleteInputError:
[scalameta] [error] override def incompleteInputError(msg: String, actions: List[scala.reflect.internal.util.CodeAction]): Unit
[scalameta] [error]       override def incompleteInputError(msg: String) = { fail = true }
[scalameta] [error]                    ^

is it remediable here in scala/scala, or do we need to just ask the scalameta folks to adjust?

@SethTisue
Copy link
Member

SethTisue commented Jul 14, 2023

it's just test code (note jvm/src/test), but since scalameta is crossbuilt for Scala minor versions, it's not as simple as just updating the code to match the new signature, since then it will no longer compile against 2.13.11 and earlier

UPDATE: Tomasz says it is that simple, actually 👍

SethTisue added a commit to scalacommunitybuild/scalameta that referenced this pull request Jul 14, 2023
LuciferYang pushed a commit to apache/spark that referenced this pull request Jul 27, 2023
### What changes were proposed in this pull request?
The pr aims to upgrade sbt from 1.9.2 to 1.9.3.

### Why are the changes needed?
1.The new version brings some improvment:
Actionable diagnostics (aka quickfix)
Actionable diagnostics, or quickfix, is an area in Scala tooling that's been getting attention since Chris Kipp presented it in the March 2023 Tooling Summit. Chris has written the [roadmap](https://contributors.scala-lang.org/t/roadmap-for-actionable-diagnostics/6172/1) and sent sbt/sbt#7242 that kickstarted the effort, but now there's been steady progress in build-server-protocol/build-server-protocol#527, scala/scala3#17337, scala/scala#10406, IntelliJ, Zinc, etc. Metals 1.0.0, for example, is now capable of surfacing code actions as a quickfix.
sbt 1.9.3 adds a new interface called AnalysisCallback2 to relay code actions from the compiler(s) to Zinc's Analysis file. Future version of Scala 2.13.x (and hopefully Scala 3) will release with proper code actions, but as a demo I've implemented a code action for procedure syntax usages even on current Scala 2.13.11 with -deprecation flag.

2.Full release notes:
https://github.com/sbt/sbt/releases/tag/v1.9.3

3.v1.9.2 VS v1.9.3
sbt/sbt@v1.9.2...v1.9.3

### Does this PR introduce _any_ user-facing change?
No.

### How was this patch tested?
Pass GA.

Closes #42141 from panbingkun/SPARK-44536.

Authored-by: panbingkun <pbk1982@gmail.com>
Signed-off-by: yangjie01 <yangjie01@baidu.com>
@SethTisue SethTisue changed the title Implement CodeAction (actionable diagnostic) Implement CodeAction (actionable diagnostic) Aug 13, 2023
@SethTisue SethTisue changed the title Implement CodeAction (actionable diagnostic) Implement actionable diagnostics, aka quickfixes (via CodeAction) Aug 23, 2023
@SethTisue SethTisue changed the title Implement actionable diagnostics, aka quickfixes (via CodeAction) Implement quickfixes, aka actionable diagnostics (via CodeAction) Aug 23, 2023
dongjoon-hyun pushed a commit to apache/spark that referenced this pull request Sep 30, 2023
### What changes were proposed in this pull request?
This pr aims to upgrade Scala from 2.13.11 to 2.13.12.

Additionally, this pr adds ``-Wconf:msg=legacy-binding:s`` to suppress similar compiler warnings as below:

```
[ERROR] /Users/yangjie01/SourceCode/git/spark-mine-13/core/src/main/scala/org/apache/spark/deploy/client/StandaloneAppClient.scala:171: reference to stop is ambiguous;
it is both defined in the enclosing class StandaloneAppClient and inherited in the enclosing class ClientEndpoint as method stop (defined in trait RpcEndpoint, inherited through parent trait ThreadSafeRpcEndpoint)
In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope.
Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.stop`.
Or use `-Wconf:msg=legacy-binding:s` to silence this warning. [quickfixable]
Applicable -Wconf / nowarn filters for this fatal warning: msg=<part of the message>, cat=other, site=org.apache.spark.deploy.client.StandaloneAppClient.ClientEndpoint.receive
```

### Why are the changes needed?
The new version bring some regression fixes:
- scala/scala#10422
- scala/scala#10424

And a new feature: Quickfixes
```
For some errors and warnings, the compiler now suggests an edit that could fix the issue. Tooling such as IDEs can then offer the edits, or the compiler itself will make the change if run again with -quickfix.
```
- scala/scala#10406
- scala/scala#10482
- scala/scala#10484

The release notes as follows:
- https://github.com/scala/scala/releases/tag/v2.13.12

### Does this PR introduce _any_ user-facing change?
Yes, Scala version changed.

### How was this patch tested?
Pass Github

### Was this patch authored or co-authored using generative AI tooling?
NO

Closes #43185 from LuciferYang/SPARK-45331.

Authored-by: yangjie01 <yangjie01@baidu.com>
Signed-off-by: Dongjoon Hyun <dhyun@apple.com>
ragnarok56 pushed a commit to ragnarok56/spark that referenced this pull request Mar 2, 2024
### What changes were proposed in this pull request?
The pr aims to upgrade sbt from 1.9.2 to 1.9.3.

### Why are the changes needed?
1.The new version brings some improvment:
Actionable diagnostics (aka quickfix)
Actionable diagnostics, or quickfix, is an area in Scala tooling that's been getting attention since Chris Kipp presented it in the March 2023 Tooling Summit. Chris has written the [roadmap](https://contributors.scala-lang.org/t/roadmap-for-actionable-diagnostics/6172/1) and sent sbt/sbt#7242 that kickstarted the effort, but now there's been steady progress in build-server-protocol/build-server-protocol#527, scala/scala3#17337, scala/scala#10406, IntelliJ, Zinc, etc. Metals 1.0.0, for example, is now capable of surfacing code actions as a quickfix.
sbt 1.9.3 adds a new interface called AnalysisCallback2 to relay code actions from the compiler(s) to Zinc's Analysis file. Future version of Scala 2.13.x (and hopefully Scala 3) will release with proper code actions, but as a demo I've implemented a code action for procedure syntax usages even on current Scala 2.13.11 with -deprecation flag.

2.Full release notes:
https://github.com/sbt/sbt/releases/tag/v1.9.3

3.v1.9.2 VS v1.9.3
sbt/sbt@v1.9.2...v1.9.3

### Does this PR introduce _any_ user-facing change?
No.

### How was this patch tested?
Pass GA.

Closes apache#42141 from panbingkun/SPARK-44536.

Authored-by: panbingkun <pbk1982@gmail.com>
Signed-off-by: yangjie01 <yangjie01@baidu.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes worth highlighting in next release notes
Projects
None yet
7 participants