All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.staticanalysis.SimplifyConsecutiveAssignments Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2024 the original author or authors.
 * 

* 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 *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* 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.staticanalysis; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.cleanup.UnnecessaryParenthesesVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; public class SimplifyConsecutiveAssignments extends Recipe { @Override public String getDisplayName() { return "Simplify consecutive assignments"; } @Override public String getDescription() { return "Combine consecutive assignments into a single statement where possible."; } @Override public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(1); } @Override public TreeVisitor getVisitor() { return new JavaIsoVisitor() { // TODO if we had a `replace()` coordinate on every `Expression`, we wouldn't need the left side of this final JavaTemplate combinedAssignment = JavaTemplate .builder("o = (#{any()} #{} #{any()});") .contextSensitive() // ok to ignore invalid type info on left-hand side of assignment. .build(); @Override public J.Block visitBlock(J.Block block, ExecutionContext ctx) { J.Block b = super.visitBlock(block, ctx); J.Block combined = b; do { b = combined; updateCursor(b); J.Block b2 = b; AtomicInteger skip = new AtomicInteger(-1); combined = b.withStatements(ListUtils.map(b.getStatements(), (i, stat) -> { if (skip.get() == i) { // this is the subsequent assignment op or unary which has been folded into a // previous statement, so drop it return null; } // is this a numeric variable assignment? String name = numericVariableName(stat); if (name != null && i < b2.getStatements().size() - 1) { Statement nextStatement = b2.getStatements().get(i + 1); Expression acc = numericVariableAccumulation(nextStatement, name); String op = numericVariableOperator(nextStatement, name); if (acc != null && op != null) { skip.set(i + 1); // combine this statement with the following statement into one binary expression return combine(new Cursor(getCursor(), stat), op, acc); } } return stat; })); } while (combined != b); if (b != block) { b = (J.Block) new UnnecessaryParenthesesVisitor() .visitNonNull(b, ctx, getCursor().getParentOrThrow()); } return b; } /** * @param s A statement * @return The name of a numeric variable being assigned or null if not a numeric * variable assignment. */ private @Nullable String numericVariableName(Statement s) { if (s instanceof J.Assignment) { return singleVariableName(((J.Assignment) s).getVariable()); } else if (s instanceof J.VariableDeclarations) { J.VariableDeclarations.NamedVariable firstNamedVariable = ((J.VariableDeclarations) s).getVariables().get(0); return firstNamedVariable.getInitializer() == null ? null : singleVariableName(firstNamedVariable.getName()); } return null; } private @Nullable Expression numericVariableAccumulation(Statement s, String name) { if (s instanceof J.Unary) { if (name.equals(singleVariableName(((J.Unary) s).getExpression()))) { return new J.Literal(Tree.randomId(), Space.EMPTY, Markers.EMPTY, 1, "1", null, JavaType.Primitive.Int); } } else if (s instanceof J.AssignmentOperation) { J.AssignmentOperation assignOp = (J.AssignmentOperation) s; if (name.equals(singleVariableName(assignOp.getVariable()))) { return assignOp.getAssignment(); } } return null; } private @Nullable String numericVariableOperator(Statement s, String name) { if (s instanceof J.Unary) { if (name.equals(singleVariableName(((J.Unary) s).getExpression()))) { switch (((J.Unary) s).getOperator()) { case PreDecrement: case PostDecrement: return "-"; case PreIncrement: case PostIncrement: return "+"; } } } else if (s instanceof J.AssignmentOperation) { J.AssignmentOperation assignOp = (J.AssignmentOperation) s; if (name.equals(singleVariableName(assignOp.getVariable()))) { switch (assignOp.getOperator()) { case Addition: return "+"; case BitAnd: return "&"; case BitOr: return "|"; case BitXor: return "^"; case Division: return "/"; case LeftShift: return "<<"; case Modulo: return "%"; case Multiplication: return "*"; case RightShift: return ">>"; case Subtraction: return "-"; case UnsignedRightShift: return ">>>"; } } } return null; } private @Nullable String singleVariableName(Expression e) { JavaType.Primitive type = TypeUtils.asPrimitive(e.getType()); return type != null && type.isNumeric() && e instanceof J.Identifier ? ((J.Identifier) e).getSimpleName() : null; } private Statement combine(Cursor cursor, String op, Expression right) { Statement s = cursor.getValue(); if (s instanceof J.Assignment) { J.Assignment assign = (J.Assignment) s; J.Assignment after = combinedAssignment.apply(cursor, s.getCoordinates().replace(), assign.getAssignment(), op, right); return assign.withAssignment(after.getAssignment()); } else if (s instanceof J.VariableDeclarations) { J.VariableDeclarations variables = (J.VariableDeclarations) s; J.Assignment after = combinedAssignment.apply(cursor, s.getCoordinates().replace(), variables.getVariables().get(0).getInitializer(), op, right); return variables.withVariables(ListUtils.map(variables.getVariables(), (i, namedVar) -> i == 0 ? namedVar.withInitializer(after.getAssignment()) : namedVar)); } throw new UnsupportedOperationException("Attempted to combine assignments into a " + "single statement with type " + s.getClass().getSimpleName()); } }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy