### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java,v retrieving revision 1.119 diff -u -r1.119 SingleNameReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 9 Sep 2010 17:36:21 -0000 1.119 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 14 Sep 2010 11:28:56 -0000 @@ -7,7 +7,8 @@ * * Contributors: * IBM Corporation - initial API and implementation - * Stephan Herrmann - Contribution for bug 292478 - Report potentially null across variable assignment + * Stephan Herrmann - Contribution for bug 292478 - Report potentially null across variable assignment, + * Contribution for bug 185682 - Increment/decrement operators mark local variables as read *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -78,10 +79,14 @@ currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); // we could improve error msg here telling "cannot use compound assignment on final local variable" } - if (isReachable) { - localBinding.useFlag = LocalVariableBinding.USED; - } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { - localBinding.useFlag = LocalVariableBinding.FAKE_USED; + if (localBinding.useFlag != LocalVariableBinding.USED) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 + // access from compound assignment does not prevent "unused" warning, unless unboxing is involved: + if (isReachable && (this.implicitConversion & TypeIds.UNBOXING) != 0) { + localBinding.useFlag = LocalVariableBinding.USED; + } else { + localBinding.useFlag = LocalVariableBinding.FAKE_USED; + } } } } @@ -462,6 +467,14 @@ * are optimized in one access: e.g "a = a + 1" optimized into "a++". */ public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 + if (!valueRequired && ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.LOCAL)) { + LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; + if (localBinding.useFlag == LocalVariableBinding.FAKE_USED) { + // compound assignment is the only usage of this local + reportOnlyUselesslyReadLocal(currentScope, localBinding); + } + } this.generateCompoundAssignment( currentScope, codeStream, @@ -662,6 +675,12 @@ return; case Binding.LOCAL : // assigning to a local variable LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 + if (!valueRequired && localBinding.useFlag == LocalVariableBinding.FAKE_USED) { + // postIncrement is the only usage of this local + reportOnlyUselesslyReadLocal(currentScope, localBinding); + } + // using incr bytecode if possible if (localBinding.type == TypeBinding.INT) { if (valueRequired) { @@ -833,6 +852,38 @@ return null; } +/* report a local/arg that is only read from a 'special operator', + * i.e., in a postIncrement expression or a compound assignment, + * where the information is never flowing out off the local/arg. */ +private void reportOnlyUselesslyReadLocal(BlockScope currentScope, LocalVariableBinding localBinding) { + if (localBinding.declaration == null) + return; // secret local + if ((localBinding.declaration.bits & ASTNode.IsLocalDeclarationReachable) == 0) + return; // declaration is unreachable + if (localBinding.declaration instanceof Argument) { + // check compiler options to report against unused arguments + if (currentScope instanceof MethodScope) { + MethodBinding method = ((MethodDeclaration)currentScope.referenceContext()).binding; + + boolean shouldReport = !method.isMain(); + if (method.isImplementing()) { + shouldReport &= currentScope.compilerOptions().reportUnusedParameterWhenImplementingAbstract; + } else if (method.isOverriding()) { + shouldReport &= currentScope.compilerOptions().reportUnusedParameterWhenOverridingConcrete; + } + + if (shouldReport) { + // report the case of an argument that is unread except through a special operator + currentScope.problemReporter().unusedArgument(localBinding.declaration); + } + } + } else { + // report the case of a local variable that is unread except for a special operator + currentScope.problemReporter().unusedLocalVariable(localBinding.declaration); + } + localBinding.useFlag = LocalVariableBinding.USED; // don't report again +} + public TypeBinding resolveType(BlockScope scope) { // for code gen, harm the restrictiveFlag #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/ProgrammingProblemsTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ProgrammingProblemsTest.java,v retrieving revision 1.23 diff -u -r1.23 ProgrammingProblemsTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/ProgrammingProblemsTest.java 14 Jul 2010 10:37:24 -0000 1.23 +++ src/org/eclipse/jdt/core/tests/compiler/regression/ProgrammingProblemsTest.java 14 Sep 2010 11:29:01 -0000 @@ -7,19 +7,21 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Stephan Herrmann - Contribution for bug 185682 - Increment/decrement operators mark local variables as read *******************************************************************************/ package org.eclipse.jdt.core.tests.compiler.regression; import java.util.HashMap; import java.util.Map; +import junit.framework.Test; + import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import junit.framework.Test; /* Collects potential programming problems tests that are not segregated in a * dedicated test class (aka NullReferenceTest). */ @@ -1683,4 +1685,250 @@ "The assignment to variable nvx has no effect\n" + "----------\n"); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 +public void test0046() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) + return; + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING); + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " int foo() {\n" + + " int i=1;\n" + + " boolean b=false;\n" + + " b|=true;\n" + // not a relevant usage + " int k = 2;\n" + + " --k;\n" + // not a relevant usage + " k+=3;\n" + // not a relevant usage + " Integer j = 3;\n" + + " j++;\n" + // relevant because unboxing is involved + " return i++;\n" + // value after increment is used + " }\n" + + "}" + }, + "----------\n" + + "1. WARNING in X.java (at line 4)\n" + + " boolean b=false;\n" + + " ^\n" + + "The local variable b is never read\n" + + "----------\n" + + "2. WARNING in X.java (at line 6)\n" + + " int k = 2;\n" + + " ^\n" + + "The local variable k is never read\n" + + "----------\n", + null/*classLibraries*/, + true/*shouldFlushOutputDirectory*/, + customOptions); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 +public void test0047() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) + return; + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.WARNING); + customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING); + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(int param1, int param2, Integer param3) {\n" + + " boolean b=false;\n" + + " b|=true;\n" + // not a relevant usage + " param1++;\n" + // not a relevant usage + " param2 += 1;\n" + // not a relevant usage + " param3++;\n" + // relevant because unboxing is involved + " }\n" + + "}" + }, + "----------\n" + + "1. WARNING in X.java (at line 2)\n" + + " void foo(int param1, int param2, Integer param3) {\n" + + " ^^^^^^\n" + + "The parameter param1 is never read\n" + + "----------\n" + + "2. WARNING in X.java (at line 2)\n" + + " void foo(int param1, int param2, Integer param3) {\n" + + " ^^^^^^\n" + + "The parameter param2 is never read\n" + + "----------\n" + + "3. WARNING in X.java (at line 3)\n" + + " boolean b=false;\n" + + " ^\n" + + "The local variable b is never read\n" + + "----------\n", + null/*classLibraries*/, + true/*shouldFlushOutputDirectory*/, + customOptions); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 +// To verify that unused parameter warning is not shown for an implementing method's parameter when +// CompilerOptions.OPTION_ReportUnusedParameterWhenImplementingAbstract is disabled +public void test0048() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) + return; + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.WARNING); + customOptions.put(CompilerOptions.OPTION_ReportUnusedParameterWhenImplementingAbstract, CompilerOptions.DISABLED); + customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING); + this.runNegativeTest( + new String[] { + "X.java", + "public class X extends A implements Y{\n" + + " public void foo(int param1, int param2, Integer param3) {\n" + // implementing method, so dont warn + " boolean b=false;\n" + + " b|=true;\n" + // not a relevant usage + " param1++;\n" + // not a relevant usage + " param2 += 1;\n" + // not a relevant usage + " param3++;\n" + // relevant because unboxing is involved + " }\n" + + " public void foo(int param1, int param2) {\n" + // warn + " boolean b=false;\n" + + " b|=true;\n" + // not a relevant usage + " param1++;\n" + // not a relevant usage + " param2 += 1;\n" + // not a relevant usage + " }\n" + + " public void bar(int param1, int param2, Integer param3) {\n" + // implementing method, so dont warn + " param1++;\n" + // not a relevant usage + " param2 += 1;\n" + // not a relevant usage + " param3++;\n" + // relevant because unboxing is involved + " }\n" + + "}\n" + + "interface Y{\n" + + " public void foo(int param1, int param2, Integer param3);" + + "}\n" + + "abstract class A{\n" + + " public abstract void bar(int param1, int param2, Integer param3);" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 3)\n" + + " boolean b=false;\n" + + " ^\n" + + "The local variable b is never read\n" + + "----------\n" + + "2. WARNING in X.java (at line 9)\n" + + " public void foo(int param1, int param2) {\n" + + " ^^^^^^\n" + + "The parameter param1 is never read\n" + + "----------\n" + + "3. WARNING in X.java (at line 9)\n" + + " public void foo(int param1, int param2) {\n" + + " ^^^^^^\n" + + "The parameter param2 is never read\n" + + "----------\n" + + "4. WARNING in X.java (at line 10)\n" + + " boolean b=false;\n" + + " ^\n" + + "The local variable b is never read\n" + + "----------\n", + null/*classLibraries*/, + true/*shouldFlushOutputDirectory*/, + customOptions); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 +// To verify that unused parameter warning is not shown for an overriding method's parameter when +// CompilerOptions.OPTION_ReportUnusedParameterWhenOverridingConcrete is disabled +public void test0049() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) + return; + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.WARNING); + customOptions.put(CompilerOptions.OPTION_ReportUnusedParameterWhenOverridingConcrete, CompilerOptions.DISABLED); + customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING); + this.runNegativeTest( + new String[] { + "X.java", + "public class X extends A {\n" + + " public void foo(int param1, int param2, Integer param3) {\n" + // overriding method, so dont warn + " boolean b=false;\n" + + " b|=true;\n" + // not a relevant usage + " param1++;\n" + // not a relevant usage + " param2 += 1;\n" + // not a relevant usage + " param3++;\n" + // relevant because unboxing is involved + " }\n" + + " public void foo(int param1, Integer param3) {\n" + // overriding method, so dont warn + " param1++;\n" + // not a relevant usage + " param3++;\n" + // relevant because unboxing is involved + " }\n" + + "}\n" + + "class A{\n" + + " public void foo(int param1, int param2, Integer param3) {\n" + + " param1 -=1;\n" + // not a relevant usage + " param2--;\n" + // not a relevant usage + " param3--;\n" + // relevant because unboxing is involved + " }\n" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 3)\n" + + " boolean b=false;\n" + + " ^\n" + + "The local variable b is never read\n" + + "----------\n" + + "2. WARNING in X.java (at line 9)\n" + + " public void foo(int param1, Integer param3) {\n" + + " ^^^^^^\n" + + "The parameter param1 is never read\n" + + "----------\n" + + "3. WARNING in X.java (at line 15)\n" + + " public void foo(int param1, int param2, Integer param3) {\n" + + " ^^^^^^\n" + + "The parameter param1 is never read\n" + + "----------\n" + + "4. WARNING in X.java (at line 15)\n" + + " public void foo(int param1, int param2, Integer param3) {\n" + + " ^^^^^^\n" + + "The parameter param2 is never read\n" + + "----------\n", + null/*classLibraries*/, + true/*shouldFlushOutputDirectory*/, + customOptions); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 +// To verify that unused local warning is not shown for locals declared in unreachable code +public void test0050() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) + return; + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING); + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " int foo() {\n" + + " int i=1;\n" + + " if (false) {\n" + + " boolean b=false;\n" + + " b|=true;\n" + + " }\n" + // not a relevant usage + " int k = 2;\n" + + " --k;\n" + // not a relevant usage + " k+=3;\n" + // not a relevant usage + " Integer j = 3;\n" + + " j++;\n" + // relevant because unboxing is involved + " return i++;\n" + // value after increment is used + " }\n" + + "}" + }, + "----------\n" + + "1. WARNING in X.java (at line 4)\n" + + " if (false) {\n" + + " boolean b=false;\n" + + " b|=true;\n" + + " }\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Dead code\n" + + "----------\n" + + "2. WARNING in X.java (at line 8)\n" + + " int k = 2;\n" + + " ^\n" + + "The local variable k is never read\n" + + "----------\n", + null/*classLibraries*/, + true/*shouldFlushOutputDirectory*/, + customOptions); +} } \ No newline at end of file