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

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

/*
 * Copyright 2021 the original author or authors.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* 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.style.Checkstyle; import org.openrewrite.java.style.NeedBracesStyle; import org.openrewrite.java.tree.*; import org.openrewrite.kotlin.tree.K; import org.openrewrite.marker.Markers; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import static java.util.Objects.requireNonNull; public class NeedBraces extends Recipe { @Override public String getDisplayName() { return "Fix missing braces"; } @Override public String getDescription() { return "Adds missing braces around code such as single-line `if`, `for`, `while`, and `do-while` block bodies."; } @Override public Set getTags() { return Collections.singleton("RSPEC-S121"); } @Override public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(2); } @Override public TreeVisitor getVisitor() { return new NeedBracesVisitor(); } private static class NeedBracesVisitor extends JavaIsoVisitor { @SuppressWarnings("NotNullFieldNotInitialized") NeedBracesStyle needBracesStyle; /** * A {@link J.Block} implies the section of code is implicitly surrounded in braces. * We can use that to our advantage by saying if you aren't a block (e.g. a single {@link Statement}, etc.), * then we're going to make this into a block. That's how we'll get the code bodies surrounded in braces. */ private J.Block buildBlock(Statement owner, T element) { J j = getCursor().getParentTreeCursor().getValue(); Space end = Space.EMPTY; if (j instanceof J.Block) { J.Block block = (J.Block) j; List statements = block.getStatements(); int i = statements.indexOf(owner); boolean last = i == statements.size() - 1; Space trailingSpace = last ? block.getEnd() : statements.get(i + 1).getPrefix(); if (!trailingSpace.getComments().isEmpty() && trailingSpace.getWhitespace().indexOf('\n') == -1) { end = trailingSpace; getCursor().getParentTreeCursor().>computeMessageIfAbsent("replaced", k -> new ArrayList<>()).add(i); } } return new J.Block( Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), element instanceof J.Empty ? Collections.emptyList() : Collections.singletonList(JRightPadded.build(element)), end ); } @Override public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof SourceFile) { SourceFile cu = (SourceFile) requireNonNull(tree); // Python don't need none of your curly braces if (cu.getSourcePath().toString().endsWith(".py")) { return (J) tree; } needBracesStyle = cu.getStyle(NeedBracesStyle.class) == null ? Checkstyle.needBracesStyle() : cu.getStyle(NeedBracesStyle.class, new NeedBracesStyle(false, false)); } return super.visit(tree, ctx); } @Override public J.Block visitBlock(J.Block block, ExecutionContext ctx) { J.Block bl = super.visitBlock(block, ctx); List indexes = getCursor().pollMessage("replaced"); if (indexes != null) { for (int index : indexes) { boolean last = index == bl.getPadding().getStatements().size() - 1; if (!last) { bl = bl.withStatements(ListUtils.map(bl.getStatements(), (i, stmt) -> { if (i == index + 1) { return stmt.withPrefix(Space.EMPTY); } return stmt; })); } else { bl = bl.withEnd(bl.getEnd().withComments(Collections.emptyList())); } } bl = maybeAutoFormat(block, bl, ctx); } return bl; } @Override public J.If visitIf(J.If iff, ExecutionContext ctx) { if (usedAsExpression()) { // Kotlin has no dedicated ternary operator return iff; } J.If elem = super.visitIf(iff, ctx); boolean hasAllowableBodyType = elem.getThenPart() instanceof J.Block; if (!needBracesStyle.getAllowSingleLineStatement() && !hasAllowableBodyType) { J.Block b = buildBlock(elem, elem.getThenPart()); elem = maybeAutoFormat(elem, elem.withThenPart(b), ctx); } return elem; } private boolean usedAsExpression() { return getCursor().getParentOrThrow().getValue() instanceof K.StatementExpression; } @Override public J.If.Else visitElse(J.If.Else else_, ExecutionContext ctx) { J.If.Else elem = super.visitElse(else_, ctx); boolean hasAllowableBodyType = elem.getBody() instanceof J.Block || elem.getBody() instanceof J.If; if (!needBracesStyle.getAllowSingleLineStatement() && !hasAllowableBodyType) { J.Block b = buildBlock(getCursor().getParentTreeCursor().getValue(), elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } return elem; } @Override public J.WhileLoop visitWhileLoop(J.WhileLoop whileLoop, ExecutionContext ctx) { J.WhileLoop elem = super.visitWhileLoop(whileLoop, ctx); boolean hasAllowableBodyType = needBracesStyle.getAllowEmptyLoopBody() ? elem.getBody() instanceof J.Block || elem.getBody() instanceof J.Empty : elem.getBody() instanceof J.Block; if (!needBracesStyle.getAllowEmptyLoopBody() && elem.getBody() instanceof J.Empty) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } else if (!needBracesStyle.getAllowSingleLineStatement() && !hasAllowableBodyType) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } return elem; } @Override public J.DoWhileLoop visitDoWhileLoop(J.DoWhileLoop doWhileLoop, ExecutionContext ctx) { J.DoWhileLoop elem = super.visitDoWhileLoop(doWhileLoop, ctx); boolean hasAllowableBodyType = needBracesStyle.getAllowEmptyLoopBody() ? elem.getBody() instanceof J.Block || elem.getBody() instanceof J.Empty : elem.getBody() instanceof J.Block; if (!needBracesStyle.getAllowEmptyLoopBody() && elem.getBody() instanceof J.Empty) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } else if (!needBracesStyle.getAllowSingleLineStatement() && !hasAllowableBodyType) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } return elem; } @Override public J.ForLoop visitForLoop(J.ForLoop forLoop, ExecutionContext ctx) { J.ForLoop elem = super.visitForLoop(forLoop, ctx); boolean hasAllowableBodyType = needBracesStyle.getAllowEmptyLoopBody() ? elem.getBody() instanceof J.Block || elem.getBody() instanceof J.Empty : elem.getBody() instanceof J.Block; if (!needBracesStyle.getAllowEmptyLoopBody() && elem.getBody() instanceof J.Empty) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } else if (!needBracesStyle.getAllowSingleLineStatement() && !hasAllowableBodyType) { J.Block b = buildBlock(elem, elem.getBody()); elem = maybeAutoFormat(elem, elem.withBody(b), ctx); } return elem; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy