TryFinallyCanBeTryWithResources: support java 9 variables : IDEA-171304

This commit is contained in:
Roman Ivanov
2018-01-26 10:59:40 +07:00
parent f40d2f8734
commit e926fc71e3
10 changed files with 403 additions and 320 deletions
@@ -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;
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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();
}
}
}
@@ -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();
}
}
}
@@ -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*/
}
}
}
@@ -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*/
}
}
}
}
@@ -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();
}
}
}
@@ -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 {
@@ -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;
}
}