mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 09:03:31 +07:00
TryFinallyCanBeTryWithResources: support java 9 variables : IDEA-171304
This commit is contained in:
+239
-320
@@ -22,20 +22,22 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.InspectionGadgetsFix;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.PsiElementOrderComparator;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
@@ -66,11 +68,9 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
}
|
||||
|
||||
private static class TryFinallyCanBeTryWithResourcesFix extends InspectionGadgetsFix {
|
||||
|
||||
public TryFinallyCanBeTryWithResourcesFix() {}
|
||||
|
||||
@Override
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("try.finally.can.be.try.with.resources.quickfix");
|
||||
}
|
||||
@@ -83,206 +83,72 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
return;
|
||||
}
|
||||
final PsiTryStatement tryStatement = (PsiTryStatement)parent;
|
||||
final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
|
||||
if (tryBlock == null) {
|
||||
return;
|
||||
PsiCodeBlock tryBlock = tryStatement.getTryBlock();
|
||||
if(tryBlock == null) return;
|
||||
Context context = Context.from(tryStatement);
|
||||
if(context == null) return;
|
||||
String resourceList = context.myResourceVariables.stream()
|
||||
.map(ResourceVariable::generateResourceDeclaration)
|
||||
.collect(Collectors.joining(";"));
|
||||
StringBuilder sb = new StringBuilder("try(");
|
||||
sb.append(resourceList).append(")");
|
||||
restoreStatements(tryStatement, tryBlock, context);
|
||||
for (PsiStatement statement : context.myStatementsToDelete) {
|
||||
new CommentTracker().deleteAndRestoreComments(statement);
|
||||
}
|
||||
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
|
||||
if (finallyBlock == null) {
|
||||
return;
|
||||
}
|
||||
final PsiElement[] tryBlockChildren = tryBlock.getChildren();
|
||||
final Set<PsiLocalVariable> variables = new LinkedHashSet<>();
|
||||
for (final PsiLocalVariable variable : collectVariables(tryStatement)) {
|
||||
if (!isVariableUsedOutsideContext(variable, tryBlock)) {
|
||||
variables.add(variable);
|
||||
for (ResourceVariable variable : context.myResourceVariables) {
|
||||
if (!variable.myUsedOutsideTry) {
|
||||
new CommentTracker().deleteAndRestoreComments(variable.myVariable);
|
||||
}
|
||||
}
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
@NonNls final StringBuilder newTryStatementText = new StringBuilder("try (");
|
||||
final Set<Integer> unwantedChildren = new HashSet<>(2);
|
||||
boolean separator = false;
|
||||
for (final PsiLocalVariable variable : variables) {
|
||||
final boolean hasInitializer;
|
||||
final PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) {
|
||||
hasInitializer = false;
|
||||
}
|
||||
else {
|
||||
final PsiType type = initializer.getType();
|
||||
hasInitializer = !PsiType.NULL.equals(type);
|
||||
}
|
||||
if (separator) {
|
||||
newTryStatementText.append(';');
|
||||
}
|
||||
newTryStatementText.append(variable.getTypeElement().getText()).append(' ').append(variable.getName()).append('=');
|
||||
if (hasInitializer) {
|
||||
newTryStatementText.append(initializer.getText());
|
||||
}
|
||||
else {
|
||||
final int index = findInitialization(tryBlockChildren, variable, false);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
unwantedChildren.add(index);
|
||||
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)tryBlockChildren[index];
|
||||
if (expressionStatement.getNextSibling() instanceof PsiWhiteSpace) {
|
||||
unwantedChildren.add(index + 1);
|
||||
}
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expressionStatement.getExpression();
|
||||
final PsiExpression rhs = assignmentExpression.getRExpression();
|
||||
if (rhs == null) {
|
||||
return;
|
||||
}
|
||||
newTryStatementText.append(rhs.getText());
|
||||
}
|
||||
separator = true;
|
||||
sb.append(tryBlock.getText());
|
||||
for (PsiCodeBlock block : tryStatement.getCatchBlocks()) {
|
||||
sb.append("catch").append(block.getText());
|
||||
}
|
||||
if (!unwantedChildren.isEmpty()) {
|
||||
int j = 1;
|
||||
while (!unwantedChildren.contains(Integer.valueOf(j)) && j < tryBlockChildren.length - 1) {
|
||||
tryStatement.getParent().addBefore(tryBlockChildren[j], tryStatement);
|
||||
unwantedChildren.add(j);
|
||||
j++;
|
||||
}
|
||||
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
|
||||
if(finallyBlock == null) return;
|
||||
if(finallyNonEmpty(finallyBlock)) {
|
||||
sb.append("finally").append(finallyBlock.getText());
|
||||
}
|
||||
newTryStatementText.append(") {");
|
||||
final int tryBlockStatementsLength = tryBlockChildren.length - 1;
|
||||
for (int i = 1; i < tryBlockStatementsLength; i++) {
|
||||
final PsiElement child = tryBlockChildren[i];
|
||||
if (unwantedChildren.contains(Integer.valueOf(i))) {
|
||||
continue;
|
||||
}
|
||||
newTryStatementText.append(child.getText());
|
||||
}
|
||||
newTryStatementText.append('}');
|
||||
final PsiCatchSection[] catchSections = tryStatement.getCatchSections();
|
||||
for (final PsiCatchSection catchSection : catchSections) {
|
||||
newTryStatementText.append(catchSection.getText());
|
||||
}
|
||||
final PsiElement[] finallyChildren = finallyBlock.getChildren();
|
||||
boolean appended = false;
|
||||
final int finallyChildrenLength = finallyChildren.length - 1;
|
||||
final List<PsiElement> savedComments = new ArrayList<>();
|
||||
for (int i = 1; i < finallyChildrenLength; i++) {
|
||||
final PsiElement child = finallyChildren[i];
|
||||
if (isCloseStatement(child, variables)) {
|
||||
continue;
|
||||
}
|
||||
if (!appended) {
|
||||
if (child instanceof PsiComment) {
|
||||
final PsiComment comment = (PsiComment)child;
|
||||
final PsiElement prevSibling = child.getPrevSibling();
|
||||
if (prevSibling instanceof PsiWhiteSpace && savedComments.isEmpty()) {
|
||||
savedComments.add(prevSibling);
|
||||
}
|
||||
savedComments.add(comment);
|
||||
final PsiElement nextSibling = child.getNextSibling();
|
||||
if (nextSibling instanceof PsiWhiteSpace) {
|
||||
savedComments.add(nextSibling);
|
||||
}
|
||||
}
|
||||
else if (!(child instanceof PsiWhiteSpace)) {
|
||||
newTryStatementText.append(" finally {");
|
||||
for (final PsiElement savedComment : savedComments) {
|
||||
newTryStatementText.append(savedComment.getText());
|
||||
}
|
||||
newTryStatementText.append(child.getText());
|
||||
appended = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newTryStatementText.append(child.getText());
|
||||
}
|
||||
}
|
||||
if (appended) {
|
||||
newTryStatementText.append('}');
|
||||
}
|
||||
for (final PsiLocalVariable variable : variables) {
|
||||
variable.delete();
|
||||
}
|
||||
if (!appended) {
|
||||
final int savedCommentsSize = savedComments.size();
|
||||
final PsiElement parent1 = tryStatement.getParent();
|
||||
for (int i = savedCommentsSize - 1; i >= 0; i--) {
|
||||
final PsiElement savedComment = savedComments.get(i);
|
||||
parent1.addAfter(savedComment, tryStatement);
|
||||
}
|
||||
}
|
||||
final PsiStatement newTryStatement = factory.createStatementFromText(newTryStatementText.toString(), element);
|
||||
tryStatement.replace(newTryStatement);
|
||||
tryStatement.replace(JavaPsiFacade.getElementFactory(project).createStatementFromText(sb.toString(), tryStatement));
|
||||
}
|
||||
|
||||
private static boolean isCloseStatement(PsiElement element, Set<PsiLocalVariable> variables) {
|
||||
if (element instanceof PsiExpressionStatement) {
|
||||
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element;
|
||||
final PsiExpression expression = expressionStatement.getExpression();
|
||||
if (!(expression instanceof PsiMethodCallExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
|
||||
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
|
||||
final String methodName = methodExpression.getReferenceName();
|
||||
if (!HardcodedMethodConstants.CLOSE.equals(methodName)) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression qualifier = methodExpression.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
if (!(target instanceof PsiLocalVariable)) {
|
||||
return false;
|
||||
}
|
||||
final PsiLocalVariable variable = (PsiLocalVariable)target;
|
||||
return variables.contains(variable);
|
||||
}
|
||||
else if (element instanceof PsiIfStatement) {
|
||||
final PsiIfStatement ifStatement = (PsiIfStatement)element;
|
||||
if (ifStatement.getElseBranch() != null) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression condition = ifStatement.getCondition();
|
||||
if (!(condition instanceof PsiBinaryExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition;
|
||||
final IElementType tokenType = binaryExpression.getOperationTokenType();
|
||||
if (!JavaTokenType.NE.equals(tokenType)) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression lhs = binaryExpression.getLOperand();
|
||||
final PsiExpression rhs = binaryExpression.getROperand();
|
||||
if (rhs == null) {
|
||||
return false;
|
||||
}
|
||||
if (PsiType.NULL.equals(rhs.getType())) {
|
||||
if (resolveLocalVariable(lhs) == null) return false;
|
||||
}
|
||||
else if (PsiType.NULL.equals(lhs.getType())) {
|
||||
if (resolveLocalVariable(rhs) == null) return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
final PsiStatement thenBranch = ifStatement.getThenBranch();
|
||||
if (thenBranch instanceof PsiExpressionStatement) {
|
||||
return isCloseStatement(thenBranch, variables);
|
||||
}
|
||||
else if (thenBranch instanceof PsiBlockStatement) {
|
||||
final PsiBlockStatement blockStatement = (PsiBlockStatement)thenBranch;
|
||||
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
|
||||
return isCloseStatement(ControlFlowUtils.getOnlyStatementInBlock(codeBlock), variables);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
private static void restoreStatements(PsiTryStatement tryStatement,
|
||||
PsiCodeBlock tryBlock,
|
||||
Context context) {
|
||||
Optional<PsiExpression> lastInTryVariable = StreamEx.of(context.myResourceVariables)
|
||||
.map(v -> v.myInitializer)
|
||||
.filter(e -> e != null && PsiTreeUtil.isAncestor(tryBlock, e, false))
|
||||
.max(PsiElementOrderComparator.getInstance());
|
||||
List<PsiStatement> elementsToRestore = new ArrayList<>();
|
||||
if(lastInTryVariable.isPresent()) {
|
||||
PsiStatement last = PsiTreeUtil.getParentOfType(lastInTryVariable.get(), PsiStatement.class);
|
||||
PsiStatement[] statements = tryBlock.getStatements();
|
||||
for (int i = 0; i < statements.length && statements[i] != last; i++) {
|
||||
PsiStatement current = statements[i];
|
||||
if(context.myStatementsToDelete.contains(current)) {
|
||||
continue;
|
||||
}
|
||||
elementsToRestore.add(current);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
PsiElement tryStatementParent = tryStatement.getParent();
|
||||
for (int i = elementsToRestore.size() - 1; i >= 0; i--) {
|
||||
PsiStatement statement = elementsToRestore.get(i);
|
||||
tryStatementParent.addBefore(statement, tryStatement);
|
||||
statement.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean finallyNonEmpty(PsiCodeBlock finallyBlock) {
|
||||
PsiElement[] children = finallyBlock.getChildren();
|
||||
for (int i = 1; i < children.length - 1; i++) {
|
||||
if (!(children[i] instanceof PsiWhiteSpace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,38 +166,114 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
@Override
|
||||
public void visitTryStatement(PsiTryStatement tryStatement) {
|
||||
super.visitTryStatement(tryStatement);
|
||||
final PsiResourceList resourceList = tryStatement.getResourceList();
|
||||
if (resourceList != null) {
|
||||
return;
|
||||
}
|
||||
final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
|
||||
if (tryBlock == null) {
|
||||
return;
|
||||
}
|
||||
final List<PsiLocalVariable> variables = collectVariables(tryStatement);
|
||||
if (variables.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final PsiStatement[] tryBlockStatements = tryBlock.getStatements();
|
||||
for (PsiVariable variable : variables) {
|
||||
final boolean hasInitializer;
|
||||
final PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) {
|
||||
hasInitializer = false;
|
||||
}
|
||||
else {
|
||||
final PsiType type = initializer.getType();
|
||||
hasInitializer = !PsiType.NULL.equals(type);
|
||||
}
|
||||
final int index = findInitialization(tryBlockStatements, variable, hasInitializer);
|
||||
if ((index >= 0) == hasInitializer || isVariableUsedOutsideContext(variable, tryBlock)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Context.from(tryStatement) == null) return;
|
||||
registerStatementError(tryStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Context {
|
||||
final @NotNull List<ResourceVariable> myResourceVariables;
|
||||
final @NotNull Set<PsiStatement> myStatementsToDelete;
|
||||
|
||||
private Context(@NotNull List<ResourceVariable> resourceVariables, @NotNull Set<PsiStatement> statementsToDelete) {
|
||||
myResourceVariables = resourceVariables;
|
||||
myStatementsToDelete = statementsToDelete;
|
||||
}
|
||||
|
||||
static @Nullable
|
||||
Context from(@NotNull PsiTryStatement tryStatement) {
|
||||
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
|
||||
if (finallyBlock == null) return null;
|
||||
PsiCodeBlock tryBlock = tryStatement.getTryBlock();
|
||||
if(tryBlock == null) return null;
|
||||
PsiStatement[] tryStatements = tryBlock.getStatements();
|
||||
PsiStatement[] finallyStatements = finallyBlock.getStatements();
|
||||
BitSet closedVariableStatementIndices = new BitSet(finallyStatements.length);
|
||||
Set<PsiVariable> collectedVariables = new HashSet<>();
|
||||
for (int i = 0, length = finallyStatements.length; i < length; i++) {
|
||||
PsiStatement statement = finallyStatements[i];
|
||||
closedVariableStatementIndices.set(i, findAutoCloseableVariables(statement, collectedVariables));
|
||||
}
|
||||
if (collectedVariables.isEmpty()) return null;
|
||||
if (resourceVariablesUsedInFinally(finallyStatements, closedVariableStatementIndices, collectedVariables)) return null;
|
||||
|
||||
List<ResourceVariable> resourceVariables = new ArrayList<>();
|
||||
List<PsiStatement> statementsToDelete = new ArrayList<>();
|
||||
for (PsiVariable resourceVariable : collectedVariables) {
|
||||
boolean variableUsedOutsideTry = isVariableUsedOutsideContext(resourceVariable, tryStatement);
|
||||
if (!PsiUtil.isLanguageLevel9OrHigher(finallyBlock) && variableUsedOutsideTry) return null;
|
||||
if (!variableUsedOutsideTry && resourceVariable instanceof PsiLocalVariable) {
|
||||
PsiExpression initializer = resourceVariable.getInitializer();
|
||||
boolean hasNonNullInitializer = initializer != null && !PsiType.NULL.equals(initializer.getType());
|
||||
if (!hasNonNullInitializer) {
|
||||
int assignmentStatementIndex = findInitialization(tryStatements, resourceVariable);
|
||||
if (assignmentStatementIndex == -1) return null;
|
||||
PsiExpressionStatement assignmentStatement = (PsiExpressionStatement)tryStatements[assignmentStatementIndex];
|
||||
PsiExpression expression = assignmentStatement.getExpression();
|
||||
PsiAssignmentExpression assignment = tryCast(expression, PsiAssignmentExpression.class);
|
||||
if (assignment == null) return null;
|
||||
initializer = assignment.getRExpression();
|
||||
if(initializer == null) return null;
|
||||
statementsToDelete.add(tryStatements[assignmentStatementIndex]);
|
||||
} else {
|
||||
if (VariableAccessUtils.variableIsAssigned(resourceVariable, tryBlock)) return null;
|
||||
}
|
||||
resourceVariables.add(new ResourceVariable(initializer, false, resourceVariable));
|
||||
} else if (((resourceVariable instanceof PsiLocalVariable && resourceVariable.getInitializer() != null) ||
|
||||
resourceVariable instanceof PsiParameter) && FinalUtils.canBeFinal(resourceVariable)) {
|
||||
resourceVariables.add(new ResourceVariable(null, true, resourceVariable));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < finallyStatements.length; i++) {
|
||||
if(closedVariableStatementIndices.get(i)) {
|
||||
statementsToDelete.add(finallyStatements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(resourceVariables, Comparator.comparing(o -> o.myVariable, PsiElementOrderComparator.getInstance()));
|
||||
return new Context(resourceVariables, new HashSet<>(statementsToDelete));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean resourceVariablesUsedInFinally(PsiStatement[] statements,
|
||||
BitSet closedVariableStatementIndices,
|
||||
Set<PsiVariable> resourceVariables) {
|
||||
for (int i = 0; i < statements.length; i++) {
|
||||
if (!closedVariableStatementIndices.get(i)) {
|
||||
Set<PsiVariable> usedVariables = VariableAccessUtils.collectUsedVariables(statements[i]);
|
||||
for (PsiVariable usedVariable : usedVariables) {
|
||||
if (resourceVariables.contains(usedVariable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class ResourceVariable {
|
||||
final @Nullable("when in java 9") PsiExpression myInitializer;
|
||||
final boolean myUsedOutsideTry; // true only if Java9 or above
|
||||
final @NotNull PsiVariable myVariable;
|
||||
|
||||
ResourceVariable(@Nullable PsiExpression initializer, boolean usedOutsideTry, @NotNull PsiVariable variable) {
|
||||
myInitializer = initializer;
|
||||
myUsedOutsideTry = usedOutsideTry;
|
||||
myVariable = variable;
|
||||
}
|
||||
|
||||
String generateResourceDeclaration() {
|
||||
if (myUsedOutsideTry) {
|
||||
return myVariable.getName();
|
||||
} else {
|
||||
assert myInitializer != null;
|
||||
return Objects.requireNonNull(myVariable.getTypeElement()).getText() + " " + myVariable.getName() + "=" + myInitializer.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isVariableUsedOutsideContext(PsiVariable variable, PsiElement context) {
|
||||
final VariableUsedOutsideContextVisitor visitor = new VariableUsedOutsideContextVisitor(variable, context);
|
||||
final PsiElement declarationScope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
|
||||
@@ -342,62 +284,31 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
return visitor.variableIsUsed();
|
||||
}
|
||||
|
||||
private static List<PsiLocalVariable> collectVariables(PsiTryStatement tryStatement) {
|
||||
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
|
||||
if (finallyBlock == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final PsiStatement[] statements = finallyBlock.getStatements();
|
||||
if (statements.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<PsiLocalVariable> variables = new ArrayList<>();
|
||||
for (PsiStatement statement : statements) {
|
||||
final PsiLocalVariable variable = findAutoCloseableVariable(statement);
|
||||
if (variable != null) {
|
||||
variables.add(variable);
|
||||
}
|
||||
}
|
||||
Collections.sort(variables, PsiElementOrderComparator.getInstance());
|
||||
return variables;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiLocalVariable findAutoCloseableVariable(PsiStatement statement) {
|
||||
private static boolean findAutoClosableVariableWithoutTry(PsiStatement statement, Set<PsiVariable> variables) {
|
||||
if (statement instanceof PsiIfStatement) {
|
||||
final PsiIfStatement ifStatement = (PsiIfStatement)statement;
|
||||
if (ifStatement.getElseBranch() != null) {
|
||||
return null;
|
||||
}
|
||||
if (ifStatement.getElseBranch() != null) return false;
|
||||
final PsiExpression condition = ifStatement.getCondition();
|
||||
if (!(condition instanceof PsiBinaryExpression)) {
|
||||
return null;
|
||||
}
|
||||
if (!(condition instanceof PsiBinaryExpression)) return false;
|
||||
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition;
|
||||
final IElementType tokenType = binaryExpression.getOperationTokenType();
|
||||
if (!JavaTokenType.NE.equals(tokenType)) {
|
||||
return null;
|
||||
}
|
||||
if (!JavaTokenType.NE.equals(tokenType)) return false;
|
||||
final PsiExpression lhs = binaryExpression.getLOperand();
|
||||
final PsiExpression rhs = binaryExpression.getROperand();
|
||||
if (rhs == null) {
|
||||
return null;
|
||||
}
|
||||
if (rhs == null) return false;
|
||||
final PsiElement variable;
|
||||
if (PsiType.NULL.equals(rhs.getType())) {
|
||||
variable = resolveLocalVariable(lhs);
|
||||
variable = ExpressionUtils.resolveLocalVariable(lhs);
|
||||
}
|
||||
else if (PsiType.NULL.equals(lhs.getType())) {
|
||||
variable = resolveLocalVariable(rhs);
|
||||
variable = ExpressionUtils.resolveLocalVariable(rhs);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
if (variable == null) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
if (variable == null) return false;
|
||||
final PsiStatement thenBranch = ifStatement.getThenBranch();
|
||||
final PsiLocalVariable resourceVariable;
|
||||
final PsiVariable resourceVariable;
|
||||
if (thenBranch instanceof PsiExpressionStatement) {
|
||||
resourceVariable = findAutoCloseableVariable(thenBranch);
|
||||
}
|
||||
@@ -407,40 +318,70 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
resourceVariable = findAutoCloseableVariable(ControlFlowUtils.getOnlyStatementInBlock(codeBlock));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
if (variable.equals(resourceVariable)) {
|
||||
return resourceVariable;
|
||||
variables.add(resourceVariable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (statement instanceof PsiExpressionStatement) {
|
||||
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement;
|
||||
final PsiExpression expression = expressionStatement.getExpression();
|
||||
if (!(expression instanceof PsiMethodCallExpression)) {
|
||||
return null;
|
||||
}
|
||||
if (!(expression instanceof PsiMethodCallExpression)) return false;
|
||||
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
|
||||
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
|
||||
final String methodName = methodExpression.getReferenceName();
|
||||
if (!HardcodedMethodConstants.CLOSE.equals(methodName)) {
|
||||
return null;
|
||||
if (HardcodedMethodConstants.CLOSE.equals(methodName)) {
|
||||
final PsiExpression qualifier = methodExpression.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression)) return false;
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
if (!(target instanceof PsiLocalVariable || target instanceof PsiParameter) || target instanceof PsiResourceVariable) return false;
|
||||
PsiVariable variable = (PsiVariable)target;
|
||||
if (!isAutoCloseable(variable)) return false;
|
||||
variables.add(variable);
|
||||
return true;
|
||||
}
|
||||
final PsiExpression qualifier = methodExpression.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression)) {
|
||||
return null;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
if (!(target instanceof PsiLocalVariable) || target instanceof PsiResourceVariable) {
|
||||
return null;
|
||||
}
|
||||
final PsiLocalVariable variable = (PsiLocalVariable)target;
|
||||
if (!isAutoCloseable(variable)) {
|
||||
return null;
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiVariable findAutoCloseableVariable(PsiStatement statement) {
|
||||
Set<PsiVariable> variables = new HashSet<>(1);
|
||||
findAutoCloseableVariables(statement, variables);
|
||||
if(variables.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return ContainerUtil.getFirstItem(variables);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean findAutoCloseableVariables(PsiStatement statement, Set<PsiVariable> variables) {
|
||||
if (findAutoClosableVariableWithoutTry(statement, variables)) return true;
|
||||
if (statement instanceof PsiTryStatement) {
|
||||
PsiTryStatement tryStatement = (PsiTryStatement)statement;
|
||||
if (tryStatement.getResourceList() != null || tryStatement.getFinallyBlock() != null) return true;
|
||||
PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
|
||||
if (catchBlocks.length != 1) return true;
|
||||
PsiStatement[] catchStatements = catchBlocks[0].getStatements();
|
||||
if (catchStatements.length != 0) return true;
|
||||
PsiCodeBlock tryBlock = tryStatement.getTryBlock();
|
||||
if (tryBlock == null) return true;
|
||||
PsiStatement[] tryStatements = tryBlock.getStatements();
|
||||
boolean containsClosedVariables = false;
|
||||
for (PsiStatement tryStmt : tryStatements) {
|
||||
if (findAutoClosableVariableWithoutTry(tryStmt, variables)) {
|
||||
containsClosedVariables = true;
|
||||
}
|
||||
}
|
||||
return containsClosedVariables;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAutoCloseable(PsiVariable variable) {
|
||||
@@ -448,44 +389,31 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
return InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE);
|
||||
}
|
||||
|
||||
private static int findInitialization(PsiElement[] elements, PsiVariable variable, boolean hasInitializer) {
|
||||
private static int findInitialization(PsiElement[] elements, PsiVariable variable) {
|
||||
int result = -1;
|
||||
final int statementsLength = elements.length;
|
||||
for (int i = 0; i < statementsLength; i++) {
|
||||
final PsiElement element = elements[i];
|
||||
if (isNormalAssignment(element, variable)) {
|
||||
if (result >= 0 && !hasInitializer) {
|
||||
if (isAssignmentToVariable(element, variable)) {
|
||||
if (result >= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (hasInitializer) {
|
||||
return i;
|
||||
}
|
||||
result = i;
|
||||
}
|
||||
else if (VariableAccessUtils.variableIsAssigned(variable, element)) {
|
||||
return hasInitializer ? i : -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isNormalAssignment(PsiElement element, PsiVariable variable) {
|
||||
if (!(element instanceof PsiExpressionStatement)) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element;
|
||||
final PsiExpression expression = expressionStatement.getExpression();
|
||||
if (!(expression instanceof PsiAssignmentExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression;
|
||||
final PsiExpression lhs = assignmentExpression.getLExpression();
|
||||
if (!(lhs instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
return variable.equals(target);
|
||||
private static boolean isAssignmentToVariable(PsiElement element, PsiVariable variable) {
|
||||
PsiExpressionStatement expressionStatement = tryCast(element, PsiExpressionStatement.class);
|
||||
if (expressionStatement == null) return false;
|
||||
PsiAssignmentExpression assignmentExpression = tryCast(expressionStatement.getExpression(), PsiAssignmentExpression.class);
|
||||
if (assignmentExpression == null) return false;
|
||||
if (assignmentExpression.getRExpression() == null) return false;
|
||||
return ExpressionUtils.isReferenceTo(assignmentExpression.getLExpression(), variable);
|
||||
}
|
||||
|
||||
private static class VariableUsedOutsideContextVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
@@ -543,13 +471,4 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection {
|
||||
return used;
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiLocalVariable resolveLocalVariable(PsiExpression expression) {
|
||||
if (!(expression instanceof PsiReferenceExpression)) {
|
||||
return null;
|
||||
}
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
return !(target instanceof PsiLocalVariable) ? null : (PsiLocalVariable)target;
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.siyeh.igfixes.migration.try_finally_can_be_try_with_resources;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
class Java9 {
|
||||
void test() throws FileNotFoundException {
|
||||
PrintStream printStream = new PrintStream("one");
|
||||
printStream.print("dffd");
|
||||
try (printStream) {
|
||||
printStream.print(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.siyeh.igfixes.migration.try_finally_can_be_try_with_resources;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
class Java9 {
|
||||
void test() throws FileNotFoundException {
|
||||
PrintStream printStream = new PrintStream("one");
|
||||
printStream.print("dffd");
|
||||
try<caret> {
|
||||
printStream.print(true);
|
||||
} finally {
|
||||
printStream.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.siyeh.igfixes.migration.try_finally_can_be_try_with_resources;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
class MyAutoCloseable implements AutoCloseable {
|
||||
|
||||
void foo() {
|
||||
System.out.println("foo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
System.out.println("close");
|
||||
}
|
||||
}
|
||||
|
||||
class Java9 {
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
test(new MyAutoCloseable());
|
||||
}
|
||||
|
||||
static void test(MyAutoCloseable m) throws FileNotFoundException {
|
||||
m.foo();
|
||||
try (m; MyAutoCloseable m1 = new MyAutoCloseable()) {
|
||||
m.foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.siyeh.igfixes.migration.try_finally_can_be_try_with_resources;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
class MyAutoCloseable implements AutoCloseable {
|
||||
|
||||
void foo() {
|
||||
System.out.println("foo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
System.out.println("close");
|
||||
}
|
||||
}
|
||||
|
||||
class Java9 {
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
test(new MyAutoCloseable());
|
||||
}
|
||||
|
||||
static void test(MyAutoCloseable m) throws FileNotFoundException {
|
||||
MyAutoCloseable m1 = new MyAutoCloseable();
|
||||
m.foo();
|
||||
try<caret> {
|
||||
m.foo();
|
||||
} finally {
|
||||
m.close();
|
||||
m1.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
import java.io.Reader;
|
||||
|
||||
class X {
|
||||
public void test() throws Exception {
|
||||
|
||||
try (Reader r = new FileReader("")) {
|
||||
r.read();
|
||||
} finally {
|
||||
// 1
|
||||
/*2*/
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
import java.io.Reader;
|
||||
|
||||
class X {
|
||||
public void test() throws Exception {
|
||||
Reader r = new FileReader("");
|
||||
|
||||
<caret>try {
|
||||
r.read();
|
||||
}
|
||||
finally {
|
||||
try { // 1
|
||||
r.close();
|
||||
} catch(Exception e) {/*2*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -3,6 +3,7 @@ package com.siyeh.igtest.migration.try_finally_can_be_try_with_resources;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
|
||||
class TryFinallyCanBeTryWithResources {
|
||||
|
||||
@@ -62,4 +63,16 @@ class TryFinallyCanBeTryWithResources {
|
||||
fileInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Java9 {
|
||||
void test() throws FileNotFoundException {
|
||||
PrintStream printStream;
|
||||
printStream = new PrintStream("");
|
||||
<warning descr="'try' can use automatic resource management">try</warning> {
|
||||
printStream.print(true);
|
||||
} finally {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.siyeh.ig.fixes.migration;
|
||||
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.IGQuickFixesTestCase;
|
||||
import com.siyeh.ig.migration.TryFinallyCanBeTryWithResourcesInspection;
|
||||
@@ -25,6 +27,14 @@ public class TryFinallyCanBeTryWithResourcesFixTest extends IGQuickFixesTestCase
|
||||
public void testMultiple() { doTest(); }
|
||||
public void testDependents() { doTest(); }
|
||||
public void testKeepResourceCallInBlock() { doTest(); }
|
||||
public void testTryInCatchBlock() { doTest(); }
|
||||
public void testJava9() { doTest(); }
|
||||
public void testJava9WithParameter() { doTest(); }
|
||||
|
||||
@Override
|
||||
protected void tuneFixture(JavaModuleFixtureBuilder builder) throws Exception {
|
||||
builder.setLanguageLevel(LanguageLevel.JDK_1_9);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
|
||||
+8
@@ -16,7 +16,9 @@
|
||||
package com.siyeh.ig.migration;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.siyeh.ig.LightInspectionTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class TryFinallyCanBeTryWithResourcesInspectionTest extends LightInspectionTestCase {
|
||||
@@ -30,4 +32,10 @@ public class TryFinallyCanBeTryWithResourcesInspectionTest extends LightInspecti
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new TryFinallyCanBeTryWithResourcesInspection();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_9;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user