### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java,v retrieving revision 1.56 diff -u -r1.56 BinaryExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 31 Jan 2006 11:19:01 -0000 1.56 +++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 27 Feb 2006 11:11:44 -0000 @@ -35,12 +35,19 @@ BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - left.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); - flowInfo = left.analyseCode(currentScope, flowContext, flowInfo). - unconditionalInits(); - right.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); - return right.analyseCode(currentScope, flowContext, flowInfo). - unconditionalInits(); + if (this.resolvedType.id == T_JavaLangString) { + return right.analyseCode(currentScope, flowContext, + left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()). + unconditionalInits(); + } + else { + left.checkNPE(currentScope, flowContext, flowInfo); + flowInfo = left.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + right.checkNPE(currentScope, flowContext, flowInfo); + return right.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + } } public void computeConstant(BlockScope scope, int leftId, int rightId) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java,v retrieving revision 1.96 diff -u -r1.96 Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 17 Feb 2006 16:09:56 -0000 1.96 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 27 Feb 2006 11:11:45 -0000 @@ -480,15 +480,12 @@ * @param scope the scope of the analysis * @param flowContext the current flow context * @param flowInfo the upstream flow info; caveat: may get modified - * @param checkString if true, a local variable of type String is checked; else - * it is skipped */ public void checkNPE(BlockScope scope, FlowContext flowContext, - FlowInfo flowInfo, boolean checkString) { + FlowInfo flowInfo) { LocalVariableBinding local = this.localVariableBinding(); if (local != null && - (local.type.tagBits & TagBits.IsBaseType) == 0 && - (checkString || local.type.id != T_JavaLangString)) { + (local.type.tagBits & TagBits.IsBaseType) == 0) { if ((this.bits & IsNonNull) == 0) { flowContext.recordUsingNullReference(scope, local, this, FlowContext.MAY_NULL, flowInfo); Index: compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java,v retrieving revision 1.40 diff -u -r1.40 ArrayReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 24 Jan 2006 10:50:38 -0000 1.40 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 27 Feb 2006 11:11:44 -0000 @@ -49,7 +49,7 @@ BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - receiver.checkNPE(currentScope, flowContext, flowInfo, true); + receiver.checkNPE(currentScope, flowContext, flowInfo); flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); return position.analyseCode(currentScope, flowContext, flowInfo); } Index: compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java,v retrieving revision 1.110 diff -u -r1.110 MessageSend.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 17 Feb 2006 16:09:56 -0000 1.110 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 27 Feb 2006 11:11:45 -0000 @@ -43,7 +43,7 @@ boolean nonStatic = !binding.isStatic(); flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits(); if (nonStatic) { - receiver.checkNPE(currentScope, flowContext, flowInfo, true); + receiver.checkNPE(currentScope, flowContext, flowInfo); } if (arguments != null) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java,v retrieving revision 1.41 diff -u -r1.41 UnaryExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 31 Jan 2006 11:19:01 -0000 1.41 +++ compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 27 Feb 2006 11:11:45 -0000 @@ -31,7 +31,7 @@ BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - this.expression.checkNPE(currentScope, flowContext, flowInfo, true); + this.expression.checkNPE(currentScope, flowContext, flowInfo); if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) { return this.expression. analyseCode(currentScope, flowContext, flowInfo). Index: compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java,v retrieving revision 1.100 diff -u -r1.100 FieldReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 17 Feb 2006 16:09:56 -0000 1.100 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 27 Feb 2006 11:11:45 -0000 @@ -99,7 +99,7 @@ boolean nonStatic = !binding.isStatic(); receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic); if (nonStatic) { - receiver.checkNPE(currentScope, flowContext, flowInfo, true); + receiver.checkNPE(currentScope, flowContext, flowInfo); } if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java,v retrieving revision 1.32 diff -u -r1.32 ForeachStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 31 Jan 2006 11:19:01 -0000 1.32 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 27 Feb 2006 11:11:45 -0000 @@ -81,7 +81,7 @@ continueLabel = new BranchLabel(); // process the element variable and collection - this.collection.checkNPE(currentScope, flowContext, flowInfo, true); + this.collection.checkNPE(currentScope, flowContext, flowInfo); flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo); FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy()); Index: compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java,v retrieving revision 1.52 diff -u -r1.52 CompoundAssignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 24 Jan 2006 10:50:37 -0000 1.52 +++ compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 27 Feb 2006 11:11:45 -0000 @@ -39,7 +39,9 @@ // record setting a variable: various scenarii are possible, setting an array reference, // a field reference, a blank final field reference, a field of an enclosing instance or // just a local variable. - lhs.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); + if (this.resolvedType.id != T_JavaLangString) { + lhs.checkNPE(currentScope, flowContext, flowInfo); + } return ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits(); } #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java,v retrieving revision 1.9 diff -u -r1.9 NullReferenceTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 20 Feb 2006 11:27:08 -0000 1.9 +++ src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 27 Feb 2006 11:11:50 -0000 @@ -33,11 +33,11 @@ // -Dcompliance=1.4 (for example) to lower it if needed static { // TESTS_NAMES = new String[] { "test011" }; -// TESTS_NUMBERS = new int[] { 729 }; +// TESTS_NUMBERS = new int[] { 48 }; // TESTS_NUMBERS = new int[] { 2999 }; // TESTS_RANGE = new int[] { 2050, -1 }; // TESTS_RANGE = new int[] { 1, 2049 }; -// TESTS_RANGE = new int[] { 449, 451 }; + TESTS_RANGE = new int[] { 120, 130 }; // TESTS_RANGE = new int[] { 900, 999 }; } @@ -853,58 +853,6 @@ ""); } -// null analysis -- strings concatenation -// JLS 15.18.1: if one of the operands is null, it is replaced by "null" -// Note: having the diagnostic could come handing when the initialization path -// is non trivial; to get the diagnostic, simply put in place an -// extraneous call to toString() -- and remove it before releasing. -public void test0045_strings_concatenation() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " String foo(String s1, String s2) {\n" + - " if (s1 == null) { /* */ };\n" + - " return s1 + s2;\n" + - " }\n" + - "}\n"}, - ""); -} - -// null analysis -- strings concatenation -public void test0046_strings_concatenation() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " String foo(String s1, String s2) {\n" + - " if (s1 == null) { /* */ };\n" + - " s1 += s2;\n" + - " return s1;\n" + - " }\n" + - "}\n"}, - ""); -} - -// null analysis -- strings concatenation -public void test0047_strings_concatenation() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " String foo(String s1) {\n" + - " if (s1 == null) { /* */ };\n" + - " return s1.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " return s1.toString();\n" + - " ^^\n" + - "The variable s1 may be null\n" + - "----------\n"); -} - // null analysis -- array public void test0050_array() { this.runConformTest( @@ -1321,6 +1269,115 @@ ""); } +// null analysis -- strings concatenation +// JLS 15.18.1: if one of the operands is null, it is replaced by "null" +// Note: having the diagnostic could come handing when the initialization path +// is non trivial; to get the diagnostic, simply put in place an +// extraneous call to toString() -- and remove it before releasing. +public void test0120_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1, String s2) {\n" + + " if (s1 == null) { /* */ };\n" + + " return s1 + s2;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +public void test0121_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1, String s2) {\n" + + " if (s1 == null) { /* */ };\n" + + " s1 += s2;\n" + + " return s1;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +public void test0122_strings_concatenation() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1) {\n" + + " if (s1 == null) { /* */ };\n" + + " return s1.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " return s1.toString();\n" + + " ^^\n" + + "The variable s1 may be null\n" + + "----------\n"); +} + +// null analysis -- strings concatenation +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=127919 +// it should suffice that the return type is String to avoid +// errors +public void test0123_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s, Object o, Integer i) {\n" + + " if (s == null || o == null || i == null) { /* */ };\n" + + " if (bar()) {\n" + + " return s + i;\n" + // quiet: i replaced by "null" if null + " }\n" + + " return o + s;\n" + // quiet: o replaced by "null" if null + " }\n" + + " boolean bar() {\n" + + " return false;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=127919 +// variant +public void test0124_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s, Object o, Integer i) {\n" + + " if (s == null || o == null || i == null) { /* */ };\n" + + " s += o;\n" + // quiet: o replaced by "null" if null + " s += i;\n" + // quiet: i replaced by "null" if null + " return s;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=127919 +// variant +public void test0125_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, Integer i) {\n" + + " System.out.println(o + (o == null ? \"\" : o.toString()));\n" + // quiet: o replaced by "null" if null + " System.out.println(i + (i == null ? \"\" : i.toString()));\n" + // quiet: o replaced by "null" if null + " }\n" + + "}\n"}, + ""); +} + // null analysis -- if/else // check that obviously unreachable code does not modify the null // status of a local