Try-with-resource support: variable usage inspection

This commit is contained in:
Roman Shevchenko
2011-02-22 20:33:24 +01:00
parent c59584e8ef
commit 0f2e1cd4f6
5 changed files with 136 additions and 47 deletions
@@ -97,7 +97,7 @@ public class DefUseInspection extends BaseLocalInspectionTool {
PsiElement context = info.getContext();
PsiVariable psiVariable = info.getVariable();
if (context instanceof PsiDeclarationStatement) {
if (context instanceof PsiDeclarationStatement || context instanceof PsiResource) {
if (!info.isRead()) {
if (!isOnTheFly) {
holder.registerProblem(psiVariable.getNameIdentifier(),
@@ -474,7 +474,8 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor {
finishElement(statement);
}
@Override public void visitDeclarationStatement(PsiDeclarationStatement statement) {
@Override
public void visitDeclarationStatement(PsiDeclarationStatement statement) {
startElement(statement);
int pc = myCurrentFlow.getSize();
PsiElement[] elements = statement.getDeclaredElements();
@@ -484,28 +485,7 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor {
element.accept(this);
}
else if (element instanceof PsiVariable) {
PsiExpression initializer = ((PsiVariable)element).getInitializer();
if (initializer != null) {
myStartStatementStack.pushStatement(initializer, false);
myEndStatementStack.pushStatement(initializer, false);
initializer.accept(this);
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
if (element instanceof PsiLocalVariable && initializer != null
|| element instanceof PsiField) {
if (element instanceof PsiLocalVariable && !myPolicy.isLocalVariableAccepted((PsiLocalVariable)element)) continue;
if (myAssignmentTargetsAreElements) {
startElement(element);
}
generateWriteInstruction((PsiVariable)element);
if (myAssignmentTargetsAreElements) {
finishElement(element);
}
}
processVariable((PsiVariable)element);
}
}
if (pc == myCurrentFlow.getSize()) {
@@ -515,6 +495,31 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor {
finishElement(statement);
}
private void processVariable(final PsiVariable element) {
final PsiExpression initializer = element.getInitializer();
if (initializer != null) {
myStartStatementStack.pushStatement(initializer, false);
myEndStatementStack.pushStatement(initializer, false);
initializer.accept(this);
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
if (element instanceof PsiLocalVariable && initializer != null ||
element instanceof PsiField) {
if (element instanceof PsiLocalVariable && !myPolicy.isLocalVariableAccepted((PsiLocalVariable)element)) return;
if (myAssignmentTargetsAreElements) {
startElement(element);
}
generateWriteInstruction(element);
if (myAssignmentTargetsAreElements) {
finishElement(element);
}
}
}
@Override public void visitDoWhileStatement(PsiDoWhileStatement statement) {
startElement(statement);
myStartStatementStack.pushStatement(statement.getBody() == null ? statement : statement.getBody(), true);
@@ -1030,17 +1035,17 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor {
myFinallyBlocks.push(finallyBlock);
}
PsiResourceList resourceList = statement.getResourceList();
if (resourceList != null) {
generateCheckedExceptionJumps(resourceList);
resourceList.accept(this);
}
PsiCodeBlock tryBlock = statement.getTryBlock();
if (tryBlock != null) {
// javac works as if all checked exceptions can occur at the top of the block
generateCheckedExceptionJumps(tryBlock);
tryBlock.accept(this);
}
PsiResourceList resourceList = statement.getResourceList();
if (resourceList != null) {
generateCheckedExceptionJumps(resourceList);
resourceList.accept(this);
}
//noinspection StatementWithEmptyBody
while (myUnhandledExceptionCatchBlocks.pop() != null) ;
@@ -1157,8 +1162,36 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor {
finishElement(statement);
}
@Override
public void visitResourceList(final PsiResourceList resourceList) {
startElement(resourceList);
@Override public void visitWhileStatement(PsiWhileStatement statement) {
final List<PsiResource> resources = resourceList.getResources();
for (PsiResource resource : resources) {
ProgressManager.checkCanceled();
resource.accept(this);
}
finishElement(resourceList);
}
@Override
public void visitResource(final PsiResource resource) {
startElement(resource);
final PsiElement resourceElement = resource.getResourceElement();
if (resourceElement instanceof PsiLocalVariable) {
processVariable((PsiLocalVariable)resourceElement);
}
else if (resourceElement instanceof PsiExpression) {
resourceElement.accept(this);
}
finishElement(resource);
}
@Override
public void visitWhileStatement(PsiWhileStatement statement) {
startElement(statement);
if (statement.getBody() == null) {
myStartStatementStack.pushStatement(statement, false);
@@ -13,15 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created by IntelliJ IDEA.
* User: max
* Date: Mar 22, 2002
* Time: 7:25:02 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.psi.controlFlow;
import com.intellij.openapi.diagnostic.Logger;
@@ -34,16 +25,20 @@ import com.intellij.util.containers.IntArrayList;
import com.intellij.util.containers.Queue;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author max
* Date: Mar 22, 2002
*/
public class DefUseUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.defUse.DefUseUtil");
private DefUseUtil() {
}
private DefUseUtil() { }
public static class Info {
private final PsiVariable myVariable;
@@ -132,6 +127,7 @@ public class DefUseUtil {
}
}
@Nullable
public static List<Info> getUnusedDefs(PsiCodeBlock body, Set<PsiVariable> outUsedVariables) {
if (body == null) {
return null;
@@ -232,12 +228,13 @@ public class DefUseUtil {
if (instruction instanceof WriteVariableInstruction) {
WriteVariableInstruction writeInstruction = (WriteVariableInstruction)instruction;
if (!defsArmed[i]) {
PsiElement context = flow.getElement(i);
context = PsiTreeUtil.getNonStrictParentOfType(context, PsiStatement.class, PsiAssignmentExpression.class,
PsiPostfixExpression.class, PsiPrefixExpression.class);
PsiElement context = PsiTreeUtil.getNonStrictParentOfType(flow.getElement(i),
PsiStatement.class, PsiAssignmentExpression.class,
PsiPostfixExpression.class, PsiPrefixExpression.class,
PsiResource.class);
PsiVariable psiVariable = writeInstruction.variable;
if (context != null && !(context instanceof PsiTryStatement)) {
if (context instanceof PsiDeclarationStatement && psiVariable.getInitializer() == null) {
if (isDeclaration(context) && psiVariable.getInitializer() == null) {
if (!assignedVariables.contains(psiVariable)) {
unusedDefs.add(new Info(psiVariable, context, false));
}
@@ -251,7 +248,11 @@ public class DefUseUtil {
}
return unusedDefs;
}
private static boolean isDeclaration(final PsiElement context) {
return context instanceof PsiDeclarationStatement ||
context instanceof PsiResource && ((PsiResource)context).getResourceElement() instanceof PsiLocalVariable;
}
@NotNull
@@ -476,5 +477,4 @@ public class DefUseUtil {
return true;
}
};
}
@@ -0,0 +1,51 @@
import java.lang.Exception;
class C {
static class MyResource implements AutoCloseable {
@Override public void close() { }
}
void m1() throws Exception {
MyResource r1;
try (r1 = new MyResource()) {
System.out.println(r1);
}
try (MyResource r2 = new MyResource()) {
System.out.println(r2);
}
MyResource r3 = new MyResource();
try (MyResource r = r3) {
System.out.println(r);
}
}
void m2() throws Exception {
MyResource r1 = <warning descr="Variable 'r1' initializer 'null' is redundant">null</warning>;
try (r1 = new MyResource()) {
System.out.println(r1);
}
try (MyResource r2 = <warning descr="Variable 'r2' initializer 'new MyResource()' is redundant">new MyResource()</warning>) {
r2 = null; // todo: check for NPE
System.out.println(r2);
}
MyResource r3 = null;
System.out.println(r3);
try (r3 = <warning descr="The value 'new MyResource()' assigned to r3 is never used">new MyResource()</warning>) { }
try (MyResource <warning descr="Variable 'r4' is never used">r4</warning> = new MyResource()) { }
try (MyResource r5 = new MyResource()) {
System.out.println(r5);
r5 = <warning descr="The value 'new MyResource()' assigned to r5 is never used">new MyResource()</warning>;
}
MyResource <warning descr="Variable 'r6' is never assigned">r6</warning>;
try (MyResource r = <error descr="Variable 'r6' might not have been initialized">r6</error>) {
System.out.println(r);
}
}
}
@@ -4,6 +4,7 @@ import com.intellij.ExtensionPoints;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.defUse.DefUseInspection;
import com.intellij.codeInspection.reference.EntryPoint;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
@@ -31,7 +32,7 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new UnusedSymbolLocalInspection(), new UncheckedWarningLocalInspection()};
return new LocalInspectionTool[]{new UnusedSymbolLocalInspection(), new UncheckedWarningLocalInspection(), new DefUseInspection()};
}
public void testDuplicateAnnotations() throws Exception {
@@ -192,6 +193,10 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
doTest(false, false);
}
public void testTryWithResourcesWarn() throws Exception {
doTest(true, false);
}
public void testSafeVarargsApplicability() throws Exception {
doTest(true, false);
}