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

Java 10+ Local-Variable Type Inference should be used RSPEC-6212 #217

Closed
yeikel opened this issue Apr 16, 2022 · 9 comments · Fixed by #218
Closed

Java 10+ Local-Variable Type Inference should be used RSPEC-6212 #217

yeikel opened this issue Apr 16, 2022 · 9 comments · Fixed by #218
Labels
good first issue Good for newcomers recipe Recipe requested

Comments

@yeikel
Copy link
Contributor

yeikel commented Apr 16, 2022

This is controversial and the Sonar team had to remove it from the default "Sonar Way" profile

https://rules.sonarsource.com/java/tag/java10/RSPEC-6212

@tkvangorder tkvangorder added enhancement New feature or request good first issue Good for newcomers labels Apr 18, 2022
@tkvangorder
Copy link
Contributor

This would be straightforward to implement with OpenRewrite, it can exist as a standalone recipe. This leverage UsesJavaVersion in its applicability test.

@tkvangorder tkvangorder added recipe Recipe requested and removed enhancement New feature or request labels Apr 19, 2022
@MBoegers
Copy link
Contributor

MBoegers commented May 1, 2023

I'd like to work on this one and would like to clarify a few things.

To be sure, do we want to implement RSPEC-6212 100% Sonar like? I would argue against it because it also suggests using var for primitives, which is not recommended, as you can see in Project Ambers Local Variable Type Inference - G7.

@MBoegers
Copy link
Contributor

MBoegers commented May 1, 2023

Things I thought about, anything to add?

  1. transform local Variables like: MyClass myClass = new MyClass(); to var myClass = new MyClass();
  2. keep null assignments like String line = null
  3. special treatment for generics and the diamond operator List<String> lines = new ArrayList<>() to var lines = new ArrayList<String>(), keep also var list = List.of() in mind
  4. We depend on prior good naming, otherwise some information will be lost. But I think that's out of scope here, right?

@timtebeek timtebeek transferred this issue from openrewrite/rewrite May 1, 2023
@timtebeek
Copy link
Contributor

Thank you for your offer to help out on this one! I think you have a decent list of restrictions in place already to limit applicability. I'd indeed say not to use this for primitives, and even then it's still not always a clear improvement, which makes it a tricky recipe to recommend.

Might be good to take into account the upcoming changes for OpenRewrite 8.0, which will have change the applicability tests for instance.

Maybe we can start with a suite of unit tests and do a round of feedback on that? Then we can more clearly decide which cases are worth covering, before we look into the finer details of the recipe implementation. Feel free to to open a draft pull request with what cases you would (not) want to cover as part of this work!

@yeikel
Copy link
Contributor Author

yeikel commented May 1, 2023

I'd like to work on this one and would like to clarify a few things.

To be sure, do we want to implement RSPEC-6212 100% Sonar like? I would argue against it because it also suggests using var for primitives, which is not recommended, as you can see in Project Ambers Local Variable Type Inference - G7.

boolean seems to be an exception for this rule, right? (so not all primitives are excluded)

MBoegers added a commit to MBoegers/rewrite-migrate-java that referenced this issue May 3, 2023
@timtebeek timtebeek linked a pull request May 3, 2023 that will close this issue
@MBoegers
Copy link
Contributor

MBoegers commented May 3, 2023

boolean seems to be an exception

Good point, not all usages of primitives are bad.

  • boolean, char and String are okay
  • float, double and long are okay, iff initialized with correct typed var aLong = 1.0L; var aFloat = 1.0F; var aDouble = 1.0; var otherDouble = 1.0D
  • short and byte are dangerous in most cases
  • reuse for initialization is dangerous static final float INITIAL = 3.0f; /*...*/ double temp = INITIAL; is possible but with var inferred as float

So for primitives I suggest:

IF String, boolean, char, float with 'F', long with 'L', double THEN
   apply var transformation
IF shot, byte THEN
   do not apply var transformation
IF initalized by variable AND left hand side type matches THEN
    apply var transformation

@MBoegers
Copy link
Contributor

MBoegers commented May 3, 2023

What about not correctly used generics?
List<> strs = new ArrayList<>(); is totally okay, if you really want a List<Object> :D same for List strs = new ArrayList();
It may occur in refactored/migrated code from older java versions.
Do no harm does not apply, because it's already there.
I can think of:

  1. print a warning and transform
  2. print a warning and don't transform
  3. transform to explicit version var strs = new ArrayList<Object>()
  4. fail the recipe

Maybe via configuration?

@timtebeek
Copy link
Contributor

What about not correctly used generics? List<> strs = new ArrayList<>(); is totally okay, if you really want a List<Object> :D same for List strs = new ArrayList(); It may occur in refactored/migrated code from older java versions. Do no harm does not apply, because it's already there.

I think it might be best to keep this recipe focused on applying var only where it makes sense and does not introduce ambiguity, and to back off from making changes in the face of uncertainty about what was originally intended. We could still add tests here, and have those assert that we do not make changes (yet).

Instead it's probably better to surface other "sub-optimal" code patterns such as incorrect generics, in a separate recipe, and give users the choice there what they want to do: find only with a marker, or transform in some way. Probably a find-only recipe could be helpful to start with, then run that through Moderne on a broad list of projects, and deduce possible remediations from there, if and only if broadly applicable.

Does that sounds like a decent approach for this one?

knutwannheden added a commit that referenced this issue Jun 16, 2023
* Add test and dummy recipe as baseline for #217

* Add tests for primitives and var keywords
Group Tests for var keywords
Add use to Recipe name

* Refine and group tests as nested tests

* Refine and group tests as nested tests

* Implement UseVarKeyword reciepe except for generics
Fix format in tests and disable generics tests

* Add Todo to implement generics and explicitly skip them for the moment

* UseVarKeyword: rename variables and improve null-checking in type checking.
UseVarKeywordTest: rename method

* Add handling of static and instance initializer blocks

* add skipping of generics types

* replace

* Extract handling of primitive variable definition for local variable type inference into own recipe

* Extract handling of Object variable definition for local variable type inference into own recipe

* Add Recipe that combines Object and Primitive handling for var. Refactor DeclarationCheck to be package private Utility. Prio used UseVarKeywordTest remains as prove of correct refactoring.

* add licences to source code

* Add DocumentedExample to tests and remove Examples from Recipe

* simplify null handling and reorder hot paths

* Apply suggestions from code review

Co-authored-by: Knut Wannheden <knut.wannheden@gmail.com>

* remove NotNull Annotaions

* rework determination if inside method and add test for edgecase

* use configuration UseJavaVersion

* Update license header

---------

Co-authored-by: Knut Wannheden <knut.wannheden@gmail.com>
@timtebeek
Copy link
Contributor

Closed #218 just now, which covers some of the above; we can continue new developments in #237, or other similar new issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers recipe Recipe requested
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants