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

Add quickfixes to some warnings and errors #10484

Merged
merged 3 commits into from
Aug 23, 2023
Merged

Conversation

lrytz
Copy link
Member

@lrytz lrytz commented Aug 4, 2023

Looked through Parser, Scanner, Namer, Typer and RefChecks.

Some ad hocery in

  • finding positions, for example from a synthetic accessor DefDef that has only an offset position to get the end of the name where to add an explicit return type
  • identifying qualifiers of a selection that require parentheses (a.toDouble vs (a + a).toDouble)

Hopefully acceptable for the use case.

@lrytz lrytz requested a review from som-snytt August 4, 2023 14:24
@scala-jenkins scala-jenkins added this to the 2.13.13 milestone Aug 4, 2023
@lrytz
Copy link
Member Author

lrytz commented Aug 4, 2023

Would be cool @deprecated("use other instead") could make it to a quick fix somehow

https://github.com/scala/scala/blob/v2.13.11/src/library/scala/Predef.scala#L351-L352

@lrytz lrytz force-pushed the moreQuickFixes branch 2 times, most recently from ce5f13e to 1b73191 Compare August 4, 2023 14:44
@som-snytt
Copy link
Contributor

scalac: the IDE you didn't know you needed. (This is not yet a proper review.)

Rewrite makes unintentionally dangerous things like lossy conversion too easy to unintentionally ignore.

Who is the intended user? Someone upgrading a project with a hundred annoying warnings?

The feature I liked on my dotty unused imports branch was rewriting the import, which is always benign.

@lrytz
Copy link
Member Author

lrytz commented Aug 4, 2023

Who is the intended user? Someone upgrading a project with a hundred annoying warnings?

This is mainly intended for IDEs. The path via -quickfix to apply changes in batches has probably very limited use, we'll see what feedback we get. Basically when a new warning becomes available or is being opted in.

Rewrite makes unintentionally dangerous things like lossy conversion too easy to unintentionally ignore.

I think fixes provided by the compiler should always maintain semantics. Let's say we add a new useful warning in a minor release, like for val d: Double = someInt / 2. In a large codebase, one might want to apply the semantics-preserving rewrite to all existing cases to allow enabling the warning (fatally) for new code being written.

The feature I liked on my dotty unused imports branch was rewriting the import, which is always benign.

Metals and IntelliJ have organize imports - what would be the difference?

@lrytz lrytz modified the milestones: 2.13.13, 2.13.12 Aug 6, 2023
@SethTisue SethTisue added the release-notes worth highlighting in next release notes label Aug 8, 2023
@SethTisue
Copy link
Member

to apply changes in batches has probably very limited use

Perhaps it's more useful than it first seems. One always has the option of letting the compiler apply the update everywhere, but then checking each one by hand/eye (e.g. with git add -p or git checkout -p) before committing anything.

@SethTisue SethTisue added the prio:hi high priority (used only by core team, only near release time) label Aug 8, 2023
@som-snytt
Copy link
Contributor

The other day, I considered that it would be nice to externalize both messages and code actions. If only we had an errno or ErrorID. I will lgtm asap after I try it out and kick a tire.

Copy link
Contributor

@som-snytt som-snytt left a comment

Choose a reason for hiding this comment

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

OK, I approve! of this tedious detail work. Not sure if the follow-up namePos will solve all the "figure out the name pos" issues.

val msg = "type application is not allowed for infix operators"
migrationWarning(offset, msg, "2.13.11",
runReporting.codeAction("use selection", pos, fix, msg))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like a lot of ceremony in the compiler, but the result is neat. I opened a ticket to let the REPL show the edit.

In this case, there is an unnecessary set of parens, a minor nitpick. ((This is a common style from the dotless era.))

scala> List(42) map[String] (_.toString)
                ^
       error: type application is not allowed for infix operators
       Scala 3 migration messages are errors under -Xsource:3. Use -Wconf / @nowarn to filter them or add -Xmigration to demote them to warnings.
       Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=scala3-migration [CodeAction(use selection,Some(type application is not allowed for infix operators),List(TextEdit(RangePosition(<console>, 0, 9, 33),List(42).map[String]((_.toString)))))]

val msg = s"Wrap `${in.name}` in backticks to use it as an identifier, it will become a keyword in Scala 3."
deprecationWarning(in.offset, msg, "2.13.7",
runReporting.codeAction("add backticks", r2p(in.offset, in.offset, in.offset + in.name.length), s"`${in.name}`", msg, expected = Some((in.name.toString, unit))))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I recently noticed on another PR that the advice is not supplied in the common case:

scala> val yield = 42
           ^
       error: illegal start of simple pattern []

scala> val enum = 42
           ^
       warning: Wrap `enum` in backticks to use it as an identifier, it will become a keyword in Scala 3.

def msg(what: String, instead: String): String = s"`val` keyword in for comprehension is $what: $instead"
if (hasEq) {
val without = "instead, bind the value without `val`"
hardMigrationWarning(in.offset, msg("deprecated", without), msg("unsupported", without), "2.10.0", actions)
} else {
val m = msg("unsupported", "just remove `val`")
syntaxError(in.offset, m, actions(m))
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why the messages differ, but it's always been confusing to me. (And I'm no longer a beginner.) Like, is there a hidden message in "bind the value without val"? Some kind of dark magic incantation? This is an example where it is so much easier to just show me the fixed code! than to explain with words.

val o = nameOffset + name.decode.length
runReporting.codeAction("remove ()", r2p(o, o, o + 2), "", msg, expected = Some(("()", unit)))
}
def warnNilary() = hardMigrationWarning(nameOffset, unaryMsg("deprecated"), unaryMsg("unsupported"), "2.13.4", action)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the edge of an edge case, but also an example where the code (to supply actions) is finicky.

The following doesn't get an action:
def unary_~( /* TODO pass context? */ ) = 42
or less weird
def unary_~ ( ) = 42

Consider the style if( ! bool ) ??? to demonstrate that some coders have special preferences.

if (!exempt())
if (!exempt()) {
val msg = "method without a parameter list overrides a method with a single empty one"
val namePos = member.pos.focus.withEnd(member.pos.point + member.decodedName.length)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'll give one of these nameposses a try locally.

context.warning(tree.pos, s"integral division is implicitly converted (widened) to floating point. Add an explicit `.to${ptSym.name}`.", WarningCategory.LintIntDivToFloat)
val msg = s"integral division is implicitly converted (widened) to floating point. Add an explicit `.to${ptSym.name}`."
context.warning(tree.pos, msg, WarningCategory.LintIntDivToFloat,
runReporting.codeAction("add conversion", tree.pos, s"(${currentUnit.sourceAt(tree.pos)}).to${ptSym.name}", msg))
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the one where we make it (too) easy to make a bad choice. If they really want it, they can do it the old-fashioned way. (Tell the intern to edit all the conversions.)

}

@Test
def constructorProcedureSyntax(): Unit = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to note that linting that the unit-valued method has parens for a param list is turned off for tests.

That makes sense as a policy because test methods are never called explicitly; though it would be even finer if only test-annotated methods were ignored.

@@ -5,5 +5,5 @@ import org.junit.runners.JUnit4

@RunWith(classOf[JUnit4])
class CodeActionTest extends AbstractCodeActionTest {
override def compilerArgs: String = "-Ystop-after:typer -Yvalidate-pos:typer -Yrangepos -deprecation"
override def compilerArgs: String = "-Ystop-after:refchecks -deprecation -Xlint"
Copy link
Contributor

Choose a reason for hiding this comment

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

Xlint enables deprecation in case that helps instead of annoying.

@Test
def infixTypeArg(): Unit = {
assertCodeSuggestion("class C { List apply[Int] 1 }", "class C { List.apply[Int](1) }")
assertCodeSuggestion("class C { List apply 1 map[Int] identity }", "class C { (List apply 1).map[Int](identity) }")
Copy link
Contributor

Choose a reason for hiding this comment

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

my previous example was List(42) map[Int] (_ + 1) type.

@lrytz
Copy link
Member Author

lrytz commented Aug 15, 2023

Travis spec build failed. Maybe transient, myabe a key expired?

3.22s$ sudo apt-get update
Hit:2 http://security.ubuntu.com/ubuntu bionic-security InRelease
Hit:3 http://apt.postgresql.org/pub/repos/apt bionic-pgdg InRelease
Get:1 http://package.perforce.com/apt/ubuntu bionic InRelease [3,655 B]
Hit:4 http://archive.ubuntu.com/ubuntu bionic InRelease
Hit:5 http://archive.ubuntu.com/ubuntu bionic-updates InRelease
Hit:6 https://public.dhe.ibm.com/software/server/POWER/Linux/toolchain/at/ubuntu bionic InRelease
Hit:7 http://archive.ubuntu.com/ubuntu bionic-backports InRelease
Err:1 http://package.perforce.com/apt/ubuntu bionic InRelease
  The following signatures were invalid: EXPKEYSIG 7123CB760FF18869 Perforce Software (Package Signing) <support+packaging@perforce.com>
Reading package lists... Done
W: GPG error: http://package.perforce.com/apt/ubuntu bionic InRelease: The following signatures were invalid: EXPKEYSIG 7123CB760FF18869 Perforce Software (Package Signing) <support+packaging@perforce.com>
E: The repository 'http://package.perforce.com/apt/ubuntu bionic InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.
The command "sudo apt-get update" failed and exited with 100 during .

@SethTisue
Copy link
Member

@lrytz needs rebase

@lrytz
Copy link
Member Author

lrytz commented Aug 16, 2023

@SethTisue @som-snytt I'm unsure about the new [quick fix available] tag in warnings / errors added in #10482, it seems noisy. See the last commit in this PR. Should I revert it, put it behind a flag, replace it with 💡 emoji, ...?

@som-snytt
Copy link
Contributor

I don't like that the [qfa] notice is verbose and pushes text right, so it makes messages harder to scan.

I wondered whether it would be OK as trailing text. warning: something [qfa].

If we had a word for fixable warning: something.

Maybe we'll need to upgrade to dotty-style message format. Or rust-style, just follow the message with the correction, annotated as a quickfix.

@lrytz lrytz force-pushed the moreQuickFixes branch 2 times, most recently from 6bb165d to 2e996a3 Compare August 17, 2023 12:05
@lrytz
Copy link
Member Author

lrytz commented Aug 17, 2023

Pushing it into the label (fixable warning:) needs to be done in the reporter. ConsoleReporter for command-line scalac, but then also the xsbti.Reporter, which is in sbt's codebase.

Adding it at the end sounds reasonable. Once you know what it means it would be nice to have something short like [qf] or 💡...

There are some multi-line messages, I guess that's OK


scala> 'hi
       ^
       warning: symbol literal is deprecated; use Symbol("hi") instead [quick fix available]

scala> println
       ^
       warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method println,
       or remove the empty argument list from its definition (Java-defined methods are exempt).
       In Scala 3, an unapplied method like this will be eta-expanded into a function. [quick fix available]

scala> List(1) map[Int] identity
               ^
       error: type application is not allowed for infix operators [quick fix available]
       Scala 3 migration messages are errors under -Xsource:3. Use -Wconf / @nowarn to filter them or add -Xmigration to demote them to warnings.
       Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=scala3-migration

I pushed that. Also added support for -quickfix:silent to omit the [quick fix available] tag in messages.

allow skipping it with `-quickfix:silent`

make more error quickfixes available to `-quickfix` rewrite by
using `runReporting.error` instead of `reporter.error`.
@lrytz
Copy link
Member Author

lrytz commented Aug 18, 2023

@SethTisue LGTY?

@som-snytt
Copy link
Contributor

som-snytt commented Aug 18, 2023

still lgtm or leb (even better). Trailing [qfa] is much tidier. I look forward to bulb emoji someday. No doubt after consuming a small amount of kibble we may have other ideas or feedback.

It seems that whenever we introduce noise, we must supply a mechanism for noise suppression. (As a general observation.)

@SethTisue
Copy link
Member

Well, I'm tempted to just hit "merge". But how about [quickfixable]? That saves some space versus [quick fix available], but without being too cryptic like [qfa].

@som-snytt
Copy link
Contributor

I'm willing to merge and allow for follow-up:

we may have other ideas or feedback

but I don't want to hit the button while Seth is in mid-conjecture.

@lrytz
Copy link
Member Author

lrytz commented Aug 18, 2023

But how about [quickfixable]?

I agree shorter is better. If you native speakers think it's fine then it's certainly fine with me.

@som-snytt
Copy link
Contributor

I think it would sound fine in German but not in English.

Further bikeshedding: emit the filter:

error: type application is not allowed for infix operators [-quickfix:cat=scala3-migration]

or whatever the syntax is. (See, I don't even remember what to do. I guess in an IDE, you just click a widget.)

@lrytz
Copy link
Member Author

lrytz commented Aug 23, 2023

It seems "quickfixable" is googleable.

@lrytz lrytz merged commit 910c59d into scala:2.13.x Aug 23, 2023
4 checks passed
@SethTisue
Copy link
Member

[-quickfix:cat=scala3-migration]

that could be neat, but it could be a separate PR

@SethTisue SethTisue removed the prio:hi high priority (used only by core team, only near release time) label Aug 23, 2023
@SethTisue SethTisue changed the title Add quick fixes to more warnings and errors Add quickfixes to some warnings and errors Aug 23, 2023
@SethTisue
Copy link
Member

SethTisue commented Aug 24, 2023

community build regression

https://scala-ci.typesafe.com/job/scala-2.13.x-jdk17-integrate-community-build/1327/artifact/logs/ammonite-build.log

[ammonite] [error] ## Exception when compiling 8 sources to /home/jenkins/workspace/scala-2.13.x-jdk17-integrate-community-build/target-0.9.20/project-builds/ammonite-5906d4f5e753635e9ef3f1b47a028f37c325b112/amm/target/scala-2.13/classes
[ammonite] [error] java.lang.UnsupportedOperationException: Position.point on NoPosition
[ammonite] [error] scala.reflect.internal.util.Position.fail(Position.scala:24)
[ammonite] [error] scala.reflect.internal.util.UndefinedPosition.point(Position.scala:102)
[ammonite] [error] scala.reflect.internal.util.UndefinedPosition.point(Position.scala:97)
[ammonite] [error] scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.checkOverride$1(RefChecks.scala:467)

I verified that when compiling the same code with 2.13.11, a "method with a single empty parameter list overrides method without any parameter list" warning is issued (that's what RefChecks.scala:467 is trying to do, is to issue that warning)

the warning generation code should be robust in the face of NoPosition, but it would also be good to look into where the NoPosition is coming from

apparently the NoPosition was already present even in 2.13.11, because in the compilation log on 2.13.11, we see this, just free-floating:

[warn] method with a single empty parameter list overrides method without any parameter list

without any information about where it's coming from in the user source

the problem is reproducible outside of dbuild by checking out the community-build-2.13 branch of https://github.com/scalacommunitybuild/ammonite, then applying this diff to build.sbt:

-  libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.3.8" cross CrossVersion.full,
+  libraryDependencies += "com.lihaoyi" % "acyclic_2.13.11" % "0.3.8",

and then doing

set ThisBuild / resolvers += "nightlies" at "https://scala-ci.typesafe.com/artifactory/scala-integration/"
++2.13.12-bin-49d5507!
amm/compile

@som-snytt
Copy link
Contributor

som-snytt commented Aug 24, 2023

I'll give one of these nameposses a try locally.

That was a rash promise.

Well, I can never get dbuild to run and I don't know how to get mill to build Ammonite with my published compiler; I tried both, but I don't have the reserves for a research project just to get something to attempt to compile.

@SethTisue
Copy link
Member

SethTisue commented Aug 24, 2023

Okay, so the underlying bug was there even in 2.13.11, and I've reported it at scala/bug#12851

And then this PR turned that bug into a crasher.

I think addressing 12851 can wait for 2.13.13.

For 2.13.12, I will submit a minimal PR that just makes this not crash the compiler.

@SethTisue
Copy link
Member

I don't have the reserves for a research project just to get something to attempt to compile

(I've updated my comment above to include non-dbuild repro instructions, but it's probably moot now since the isolation work is done.)

@som-snytt
Copy link
Contributor

Thanks, I will try that later! more rash promises.

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>
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
4 participants