Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.migrate.lang.var;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesJavaVersion;
import org.openrewrite.java.tree.*;

import java.util.List;

/**
* Replaces explicit type declarations with {@code var} keyword when the initializer
* is a constructor call with an exactly matching type.
*
* <p>This recipe is more conservative than {@link UseVarForObject} and
* {@link UseVarForGenericsConstructors}. It only transforms when the declared type
* exactly matches the constructor type, avoiding cases where the declared type is an
* interface or supertype.
*/
@Value
@EqualsAndHashCode(callSuper = false)
public class UseVarForConstructors extends Recipe {

String displayName = "Use `var` for constructor call assignments";

String description = "Replace explicit type declarations with `var` when the variable is initialized with a " +
"constructor call of exactly the same type. Does not transform when declared type " +
"differs from constructor type (e.g., interface vs implementation).";

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesJavaVersion<>(10), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations varDecl, ExecutionContext ctx) {
J.VariableDeclarations vd = super.visitVariableDeclarations(varDecl, ctx);

if (!DeclarationCheck.isVarApplicable(getCursor(), vd)) {
return vd;
}

Expression initializer = vd.getVariables().get(0).getInitializer();
if (initializer == null) {
return vd;
}
initializer = initializer.unwrap();

// Only transform constructor calls
if (!(initializer instanceof J.NewClass)) {
return vd;
}

// Declared type must exactly match constructor type, as to not have additional methods become available
if (!TypeUtils.isOfType(vd.getType(), initializer.getType())) {
return vd;
}

if (vd.getType() instanceof JavaType.FullyQualified) {
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
}

return DeclarationCheck.transformToVar(vd, (J.NewClass nc) -> maybeTransferTypeArguments(vd, nc));
}

private J.NewClass maybeTransferTypeArguments(J.VariableDeclarations vd, J.NewClass initializer) {
TypeTree typeExpression = vd.getTypeExpression();
if (!(typeExpression instanceof J.ParameterizedType)) {
return initializer;
}
J.ParameterizedType paramType = (J.ParameterizedType) typeExpression;

List<Expression> declaredTypeParams = paramType.getTypeParameters();
if (declaredTypeParams == null || declaredTypeParams.isEmpty()) {
return initializer;
}

TypeTree constructorClazz = initializer.getClazz();
if (!(constructorClazz instanceof J.ParameterizedType)) {
return initializer;
}
J.ParameterizedType constructorParamType = (J.ParameterizedType) constructorClazz;

List<Expression> constructorTypeParams = constructorParamType.getTypeParameters();
boolean nullEmptyOrDiamondOperator = constructorTypeParams == null ||
constructorTypeParams.isEmpty() ||
constructorTypeParams.stream().allMatch(J.Empty.class::isInstance);
if (nullEmptyOrDiamondOperator) {
return initializer.withClazz(constructorParamType.withTypeParameters(declaredTypeParams));
}

return initializer;
}
});
}
}
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/rewrite/recipes.csv
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.l
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.UseVar,Use local variable type inference,"Apply local variable type inference (`var`) for primitives and objects. These recipes can cause unused imports, be advised to run `org.openrewrite.java.RemoveUnusedImports afterwards.",5,,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.FindVirtualThreadOpportunities,Find Virtual Thread opportunities,Find opportunities to convert existing code to use Virtual Threads.,10,,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.java.table.MethodCalls"",""displayName"":""Method calls"",""description"":""The text of matching method invocations."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""method"",""type"":""String"",""displayName"":""Method call"",""description"":""The text of the method call.""},{""name"":""className"",""type"":""String"",""displayName"":""Class name"",""description"":""The class name of the method call.""},{""name"":""methodName"",""type"":""String"",""displayName"":""Method name"",""description"":""The method name of the method call.""},{""name"":""argumentTypes"",""type"":""String"",""displayName"":""Argument types"",""description"":""The argument types of the method call.""}]}]"
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.FindNonVirtualExecutors,Find non-virtual `ExecutorService` creation,Find all places where static `java.util.concurrent.Executors` method creates a non-virtual `java.util.concurrent.ExecutorService`. This recipe can be used to search fro `ExecutorService` that can be replaced by Virtual Thread executor.,7,,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.java.table.MethodCalls"",""displayName"":""Method calls"",""description"":""The text of matching method invocations."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""method"",""type"":""String"",""displayName"":""Method call"",""description"":""The text of the method call.""},{""name"":""className"",""type"":""String"",""displayName"":""Class name"",""description"":""The class name of the method call.""},{""name"":""methodName"",""type"":""String"",""displayName"":""Method name"",""description"":""The method name of the method call.""},{""name"":""argumentTypes"",""type"":""String"",""displayName"":""Argument types"",""description"":""The argument types of the method call.""}]}]"
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.var.UseVarForConstructors,Use `var` for constructor call assignments,Replace explicit type declarations with `var` when the variable is initialized with a constructor call of exactly the same type. Does not transform when declared type differs from constructor type (e.g. interface vs implementation).,1,Var,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.var.UseVarForLiterals,Use `var` for literal assignments,Replace explicit type declarations with `var` when the variable is initialized with a String literal. Primitive literals are handled by `UseVarForPrimitive`.,1,Var,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.var.UseVarForGenericMethodInvocations,Apply `var` to generic method invocations,"Apply `var` to variables initialized by invocations of generic methods. This recipe ignores generic factory methods without parameters, because open rewrite cannot handle them correctly ATM.",1,Var,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.var.UseVarForGenericsConstructors,Apply `var` to Generic Constructors,Apply `var` to generics variables initialized by constructor calls.,1,Var,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.var.UseVarForObject,Use `var` for reference-typed variables,Try to apply local variable type inference `var` to variables containing Objects where possible. This recipe will not touch variable declarations with generics or initializers containing ternary operators.,1,Var,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,
Expand Down
Loading
Loading