StatementSwitchToExpressionSwitch: only trigger on compatible target versions#3646
Conversation
…versions This change introduces a `TargetVersion` utility class, analogous to the existing `RuntimeVersion` class. The new class tells whether the current compilation process targets a bytecode version that is generally associated with a given (or more recent) JDK version. The new utility class is used to make sure that `StatementSwitchToExpressionSwitch` flags statement switches only if the target version supports expression switches, irrespective of whether the current runtime supports such switch types. Concretely this makes the check compatible with projects that target JDK 11, yet support building with JDK 17.
c7a23da to
2a0bb07
Compare
|
Thanks, SGTM overall. I appreciate making javac has a built-in API for detecting if features are available at the current source version, what do you think about using that instead, e.g.? That's using the source version instead of the target version, which wouldn't enable the check for configurations like |
Stephan202
left a comment
There was a problem hiding this comment.
Added some notes.
| @@ -0,0 +1,88 @@ | |||
| /* | |||
| * Copyright 2023 The Error Prone Authors. | |||
There was a problem hiding this comment.
Seems safe to assume that this PR won't be merged before the fireworks ;)
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg> | ||
| <arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg> |
There was a problem hiding this comment.
Summary of changes:
- Sorted.
- Removed duplicate
com.sun.tools.javac.utilentry. - Added
com.sun.tools.javac.jvm.
| -Xmx1g | ||
| --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED | ||
| --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED | ||
| --add-exports=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED |
There was a problem hiding this comment.
If this PR is accepted, then the documentation should also be updated to include this flag.
Interesting idea! Let me have a look a this and report back here 👍 |
Stephan202
left a comment
There was a problem hiding this comment.
Will check whether this can be nicely tested without too much hassle.
| private static final ImmutableMap<String, Feature> KNOWN_FEATURES = | ||
| Maps.uniqueIndex(Arrays.asList(Feature.values()), Enum::name); |
There was a problem hiding this comment.
The string-based lookup is necessary because older runtimes won't recognize newer enum values. A bit fragile, but no more fragile than what's usual for Error Prone.
There was a problem hiding this comment.
Thanks, I didn't consider that when making the suggestion. This looks like a good way to deal with that.
One more suggestion would be to keep the current API, but avoid relying on the Feature enum, e.g.:
public static boolean supportsSwitchExpressions(Context context) {
return Source.instance(state.context).compareTo(Source.lookup("14")) >= 0;
}That means we can't use javac's knowledge about which features were used in which versions, but it also avoids depending on the string names of the Feature constants, and they're an internal API that could change (e.g. constants will probably be removed as javac drops support for old version features).
There was a problem hiding this comment.
Works for me; I can see how this reduces the maintenance burden. The Source.lookup call does require a null check; will keep the extra method for that purpose.
| * | ||
| * @see RuntimeVersion | ||
| */ | ||
| public final class SourceVersion { |
There was a problem hiding this comment.
Could also name the class something like SourceFeature(s), though that might perhaps also call for slightly different method names. 🤔
| // Expression switches finalized in Java 14 | ||
| if (!RuntimeVersion.isAtLeast14()) { | ||
| if (!SourceVersion.supportsSwitchExpressions(state.context)) { |
There was a problem hiding this comment.
Dropped the comment because the code is now self-documenting.
Stephan202
left a comment
There was a problem hiding this comment.
Added some unit tests.
| /** Returns true if the compiler source version level supports text blocks. */ | ||
| public static boolean supportsTextBlocks(Context context) { | ||
| return supportsFeature("TEXT_BLOCKS", context); | ||
| } |
There was a problem hiding this comment.
The reason for adding this method is twofold:
- With two examples it should be clear for future devs how other methods may be added.
- I have a use case for this particular feature in Update all tests to use text blocks PicnicSupermarket/error-prone-support#198.
There was a problem hiding this comment.
👍
There is at least one other use of Source in Error Prone that can also be cleaned up to use this approach. (That's just an FYI, it doesn't need to happen in this PR.)
There was a problem hiding this comment.
I don't mind adding that to the PR, but I'm not 100% sure what the extra method would have to be called. (My best guess is that this relates to EFFECTIVELY_FINAL_IN_INNER_CLASSES, but I'm not at all sure.)
| /** Returns true if the compiler source version level supports text blocks. */ | ||
| public static boolean supportsTextBlocks(Context context) { | ||
| return supportsFeature("TEXT_BLOCKS", context); | ||
| } |
There was a problem hiding this comment.
I don't mind adding that to the PR, but I'm not 100% sure what the extra method would have to be called. (My best guess is that this relates to EFFECTIVELY_FINAL_IN_INNER_CLASSES, but I'm not at all sure.)
| private static final ImmutableMap<String, Feature> KNOWN_FEATURES = | ||
| Maps.uniqueIndex(Arrays.asList(Feature.values()), Enum::name); |
There was a problem hiding this comment.
Works for me; I can see how this reduces the maintenance burden. The Source.lookup call does require a null check; will keep the extra method for that purpose.
|
(Updated the PR description to be in line with the latest revision.) Tnx for the rapid review iterations @cushon! |
…versions This change introduces a `SourceVersion` utility class, analogous to the existing `RuntimeVersion` class. The new class tells whether selected language features are available for the source version used by the compiler. The new utility class is used to make sure that `StatementSwitchToExpressionSwitch` flags statement switches only if the source version supports expression switches, irrespective of whether the current runtime supports such switch types. Concretely this makes the check compatible with projects that target JDK 11, yet support building with JDK 17. Fixes #3646 FUTURE_COPYBARA_INTEGRATE_REVIEW=#3646 from PicnicSupermarket:bugfix/dont-suggest-switch-expressions-pre-jdk14 641c9dc PiperOrigin-RevId: 498755371
This MR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [com.google.errorprone:error_prone_core](https://errorprone.info) ([source](https://github.com/google/error-prone)) | | minor | `2.16` -> `2.18.0` | | [com.google.errorprone:error_prone_annotations](https://errorprone.info) ([source](https://github.com/google/error-prone)) | compile | minor | `2.16` -> `2.18.0` | | [org.apache.maven.plugins:maven-failsafe-plugin](https://maven.apache.org/surefire/) | build | patch | `3.0.0-M7` -> `3.0.0-M8` | | [org.apache.maven.plugins:maven-surefire-plugin](https://maven.apache.org/surefire/) | build | patch | `3.0.0-M7` -> `3.0.0-M8` | --- ### Release Notes <details> <summary>google/error-prone</summary> ### [`v2.18.0`](https://github.com/google/error-prone/releases/tag/v2.18.0): Error Prone 2.18.0 [Compare Source](google/error-prone@v2.17.0...v2.18.0) New Checkers: - [`InjectOnBugCheckers`](https://errorprone.info/bugpattern/InjectOnBugCheckers) - [`LabelledBreakTarget`](https://errorprone.info/bugpattern/LabelledBreakTarget) - [`UnusedLabel`](https://errorprone.info/bugpattern/UnusedLabel) - [`YodaCondition`](https://errorprone.info/bugpattern/YodaCondition) Fixes issues: [#​1650](google/error-prone#1650), [#​2706](google/error-prone#2706), [#​3404](google/error-prone#3404), [#​3493](google/error-prone#3493), [#​3504](google/error-prone#3504), [#​3519](google/error-prone#3519), [#​3579](google/error-prone#3579), [#​3610](google/error-prone#3610), [#​3632](google/error-prone#3632), [#​3638](google/error-prone#3638), [#​3645](google/error-prone#3645), [#​3646](google/error-prone#3646), [#​3652](google/error-prone#3652), [#​3690](google/error-prone#3690) **Full Changelog**: google/error-prone@v2.17.0...v2.18.0 ### [`v2.17.0`](https://github.com/google/error-prone/releases/tag/v2.17.0): Error Prone 2.17.0 [Compare Source](google/error-prone@v2.16...v2.17.0) New Checkers: - [`AvoidObjectArrays`](https://errorprone.info/bugpattern/AvoidObjectArrays) - [`Finalize`](https://errorprone.info/bugpattern/Finalize) - [`IgnoredPureGetter`](https://errorprone.info/bugpattern/IgnoredPureGetter) - [`ImpossibleNullComparison`](https://errorprone.info/bugpattern/ProtoFieldNullComparison) - [`MathAbsoluteNegative`](https://errorprone.info/bugpattern/MathAbsoluteNegative) - [`NewFileSystem`](https://errorprone.info/bugpattern/NewFileSystem) - [`StatementSwitchToExpressionSwitch`](https://errorprone.info/bugpattern/StatementSwitchToExpressionSwitch) - [`UnqualifiedYield`](https://errorprone.info/bugpattern/UnqualifiedYield) Fixed issues: [#​2321](google/error-prone#2321), [#​3144](google/error-prone#3144), [#​3297](google/error-prone#3297), [#​3428](google/error-prone#3428), [#​3437](google/error-prone#3437), [#​3462](google/error-prone#3462), [#​3482](google/error-prone#3482), [#​3494](google/error-prone#3494) **Full Changelog**: google/error-prone@v2.16...v2.17.0 </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This MR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4yNC4wIiwidXBkYXRlZEluVmVyIjoiMzQuMjQuMCJ9-->
This change introduces a
SourceVersionutility class, analogous to theexisting
RuntimeVersionclass. The new class tells whether selected languagefeatures are available for the source version used by the compiler.
The new utility class is used to make sure that
StatementSwitchToExpressionSwitchflags statement switches only if the sourceversion supports expression switches, irrespective of whether the current
runtime supports such switch types. Concretely this makes the check compatible
with projects that target JDK 11, yet support building with JDK 17.