Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal')
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java32
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java26
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java29
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java32
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java78
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java35
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java89
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NameReference.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullLiteral.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OperatorExpression.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java100
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java49
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java63
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java24
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java14
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java72
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java22
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BaseTypeBinding.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java43
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java23
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java9
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java38
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java142
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java33
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedAnnotationBinding.java12
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java14
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java188
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties17
59 files changed, 1159 insertions, 276 deletions
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
index b07cdfd8d..b870f7f9d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -1960,6 +1960,9 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contentsOffset = startingContentsOffset;
return;
}
+ if (annotationTypeBinding.isMemberType()) {
+ this.recordInnerClasses(annotationTypeBinding);
+ }
final int typeIndex = this.constantPool.literalIndex(annotationTypeBinding.signature());
this.contents[this.contentsOffset++] = (byte) (typeIndex >> 8);
this.contents[this.contentsOffset++] = (byte) typeIndex;
@@ -2173,6 +2176,12 @@ public class ClassFile implements TypeConstants, TypeIds {
if (defaultValueBinding == null) {
this.contentsOffset = attributeOffset;
} else {
+ if (defaultValueBinding.isMemberType()) {
+ this.recordInnerClasses(defaultValueBinding);
+ }
+ if (memberValuePairReturnType.isMemberType()) {
+ this.recordInnerClasses(memberValuePairReturnType);
+ }
if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) {
// automatic wrapping
if (this.contentsOffset + 3 >= this.contents.length) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
index e55357865..2b634b2aa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -17,6 +17,7 @@
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 374605 - Unreasonable warning for enum-based switch statements
* bug 384870 - [compiler] @Deprecated annotation not detected if preceded by other annotation
+ * bug 393719 - [compiler] inconsistent warnings on iteration variables
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -129,6 +130,7 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
// for local decls
public static final int IsArgument = Bit3;
+ public static final int IsForeachElementVariable = Bit5;
// for name refs or local decls
public static final int FirstAssignmentToLocal = Bit4;
@@ -541,8 +543,9 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
return false;
ReferenceBinding refType = (ReferenceBinding) type;
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=397888
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
- if (refType instanceof TypeVariableBinding) {
+ if ((this.bits & ASTNode.InsideJavadoc) == 0 && refType instanceof TypeVariableBinding) {
refType.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
}
// ignore references insing Javadoc comments
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
index 859ee0515..47f3bd825 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -12,6 +12,7 @@
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -433,6 +434,10 @@ public abstract class Annotation extends Expression {
FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField);
recordSuppressWarnings(scope, fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
}
+ if ((sourceField.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
+ scope.problemReporter().contradictoryNullAnnotations(this);
+ sourceField.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
+ }
break;
case Binding.LOCAL :
LocalVariableBinding variable = (LocalVariableBinding) this.recipient;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java
index 55d4cac4e..21f7e7aea 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -167,7 +168,7 @@ public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream
codeStream.arrayAtPut(this.resolvedType.id, false);
}
-public int nullStatus(FlowInfo flowInfo) {
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
return FlowInfo.UNKNOWN;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
index da88a1264..75b294a6c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
@@ -22,12 +22,16 @@
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
@@ -85,9 +89,10 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
}
FlowInfo preInitInfo = null;
+ CompilerOptions compilerOptions = currentScope.compilerOptions();
boolean shouldAnalyseResource = local != null
&& flowInfo.reachMode() == FlowInfo.REACHABLE
- && currentScope.compilerOptions().analyseResourceLeaks
+ && compilerOptions.analyseResourceLeaks
//{ObjectTeams: notably lift methods of Closeable roles would trigger warnings against synthetic code
&& !currentScope.isGeneratedScope()
// SH}
@@ -108,14 +113,29 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
else
FakedTrackingVariable.cleanUpAfterAssignment(currentScope, this.lhs.bits, this.expression);
- int nullStatus = this.expression.nullStatus(flowInfo);
+ int nullStatus = this.expression.nullStatus(flowInfo, flowContext);
if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
if (nullStatus == FlowInfo.NULL) {
flowContext.recordUsingNullReference(currentScope, local, this.lhs,
FlowContext.CAN_ONLY_NULL | FlowContext.IN_ASSIGNMENT, flowInfo);
}
}
- nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, local, nullStatus, this.expression, this.expression.resolvedType);
+ if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
+ VariableBinding var = this.lhs.nullAnnotatedVariableBinding();
+ if (var != null) {
+ nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, var, nullStatus, this.expression, this.expression.resolvedType);
+ if (nullStatus == FlowInfo.NON_NULL
+ && var instanceof FieldBinding
+ && this.lhs instanceof Reference
+ && compilerOptions.enableSyntacticNullAnalysisForFields)
+ {
+ int timeToLive = (this.bits & InsideExpressionStatement) != 0
+ ? 2 // assignment is statement: make info survives the end of this statement
+ : 1; // assignment is expression: expire on next event.
+ flowContext.recordNullCheckedFieldReference((Reference) this.lhs, timeToLive);
+ }
+ }
+ }
if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
flowInfo.markNullStatus(local, nullStatus);
if (flowContext.initsOnFinally != null)
@@ -167,8 +187,8 @@ FieldBinding getLastField(Expression someExpression) {
return null;
}
-public int nullStatus(FlowInfo flowInfo) {
- return this.expression.nullStatus(flowInfo);
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
+ return this.expression.nullStatus(flowInfo, flowContext);
}
public StringBuffer print(int indent, StringBuffer output) {
@@ -227,7 +247,7 @@ public TypeBinding resolveType(BlockScope scope) {
scope.compilationUnitScope().recordTypeConversion(lhsType, rhsType);
}
if (this.expression.isConstantValueOfTypeAssignableToType(rhsType, lhsType)
- || rhsType.isCompatibleWith(lhsType)) {
+ || rhsType.isCompatibleWith(lhsType, scope)) {
this.expression.computeConversion(scope, lhsType, rhsType);
checkAssignment(scope, lhsType, rhsType);
if (this.expression instanceof CastExpression
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java
index 1a1a3674f..1c4eaa1c0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java
@@ -10,6 +10,7 @@
* Technical University Berlin - extended API and implementation
* Stephan Herrmann - Contribution for
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -71,8 +72,15 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
} else {
this.left.checkNPE(currentScope, flowContext, flowInfo);
flowInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
+ if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
+ flowContext.expireNullCheckedFieldInfo();
+ }
this.right.checkNPE(currentScope, flowContext, flowInfo);
- return this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
+ flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
+ if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
+ flowContext.expireNullCheckedFieldInfo();
+ }
+ return flowInfo;
}
} finally {
// account for exception possibly thrown by arithmetics
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
index f9e073e5e..8cf78aa9c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
@@ -14,6 +14,7 @@
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -47,6 +48,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
// empty block
if (this.statements == null) return flowInfo;
int complaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
+ boolean enableSyntacticNullAnalysisForFields = currentScope.compilerOptions().enableSyntacticNullAnalysisForFields;
for (int i = 0, max = this.statements.length; i < max; i++) {
Statement stat = this.statements[i];
if ((complaintLevel = stat.complainIfUnreachable(flowInfo, this.scope, complaintLevel, true)) < Statement.COMPLAINED_UNREACHABLE) {
@@ -55,6 +57,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
// record the effect of stat on the finally block of an enclosing try-finally, if any:
if (flowContext.initsOnFinally != null)
flowContext.mergeFinallyNullInfo(flowInfo);
+ if (enableSyntacticNullAnalysisForFields) {
+ flowContext.expireNullCheckedFieldInfo();
+ }
}
if (this.explicitDeclarations > 0) {
// if block has its own scope analyze tracking vars now:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
index ca964cae8..5fe03b0b2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
@@ -14,6 +14,8 @@
* Stephan Herrmann - Contributions for
* bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -165,7 +167,7 @@ public static void checkNeedForAssignedCast(BlockScope scope, TypeBinding expect
// double d = (float) n; // cast to float is unnecessary
if (castedExpressionType == null || rhs.resolvedType.isBaseType()) return;
//if (castedExpressionType.id == T_null) return; // tolerate null expression cast
- if (castedExpressionType.isCompatibleWith(expectedType)) {
+ if (castedExpressionType.isCompatibleWith(expectedType, scope)) {
scope.problemReporter().unnecessaryCast(rhs);
}
}
@@ -549,8 +551,8 @@ public LocalVariableBinding localVariableBinding() {
return this.expression.localVariableBinding();
}
-public int nullStatus(FlowInfo flowInfo) {
- return this.expression.nullStatus(flowInfo);
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
+ return this.expression.nullStatus(flowInfo, flowContext);
}
/**
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java
index fb166d1fc..e46fbaaf9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java
@@ -9,6 +9,8 @@
* IBM Corporation - initial API and implementation
* Technical University Berlin - extended API and implementation
* Patrick Wienands <pwienands@abit.de> - Contribution for bug 393749
+ * Stephan Herrmann - Contribution for
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -77,17 +79,23 @@ public class Clinit extends AbstractMethodDeclaration {
flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn);
FieldBinding[] fields = this.scope.enclosingSourceType().fields();
for (int i = 0, count = fields.length; i < count; i++) {
- FieldBinding field;
- if ((field = fields[i]).isStatic()
- && field.isFinal()
+ FieldBinding field = fields[i];
//{ObjectTeams: don't check copied fields:
- && (field.copyInheritanceSrc == null)
+ if (field.copyInheritanceSrc != null) continue;
// SH}
- && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
- this.scope.problemReporter().uninitializedBlankFinalField(
- field,
- this.scope.referenceType().declarationOf(field.original()));
- // can complain against the field decl, since only one <clinit>
+ if (field.isStatic()) {
+ if (!flowInfo.isDefinitelyAssigned(field)) {
+ if (field.isFinal()) {
+ this.scope.problemReporter().uninitializedBlankFinalField(
+ field,
+ this.scope.referenceType().declarationOf(field.original()));
+ // can complain against the field decl, since only one <clinit>
+ } else if (field.isNonNull()) {
+ this.scope.problemReporter().uninitializedNonNullField(
+ field,
+ this.scope.referenceType().declarationOf(field.original()));
+ }
+ }
}
}
// check static initializers thrown exceptions
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java
index 11074ddfc..3ab010e5c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -77,7 +78,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
-public int nullStatus(FlowInfo flowInfo) {
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
return FlowInfo.NON_NULL;
// we may have complained on checkNPE, but we avoid duplicate error
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
index 3dc331b33..1f1c38f0d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
@@ -17,6 +17,7 @@
* bug 354554 - [null] conditional with redundant condition yields weak error message
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -85,6 +86,14 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
this.trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
trueFlowInfo = this.valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
+ // may need to fetch this null status before expireNullCheckedFieldInfo():
+ int preComputedTrueNullStatus = -1;
+ if (currentScope.compilerOptions().enableSyntacticNullAnalysisForFields) {
+ preComputedTrueNullStatus = this.valueIfTrue.nullStatus(trueFlowInfo, flowContext);
+ // wipe information that was meant only for valueIfTrue:
+ flowContext.expireNullCheckedFieldInfo();
+ }
+
// process the if-false part
FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
if (isConditionOptimizedTrue) {
@@ -104,10 +113,14 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
FlowInfo mergedInfo;
if (isConditionOptimizedTrue){
mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
- this.nullStatus = this.valueIfTrue.nullStatus(trueFlowInfo);
+ if (preComputedTrueNullStatus != -1) {
+ this.nullStatus = preComputedTrueNullStatus;
+ } else {
+ this.nullStatus = this.valueIfTrue.nullStatus(trueFlowInfo, flowContext);
+ }
} else if (isConditionOptimizedFalse) {
mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
- this.nullStatus = this.valueIfFalse.nullStatus(falseFlowInfo);
+ this.nullStatus = this.valueIfFalse.nullStatus(falseFlowInfo, flowContext);
} else {
// this block must meet two conflicting requirements (see https://bugs.eclipse.org/324178):
// (1) For null analysis of "Object o2 = (o1 != null) ? o1 : new Object();" we need to distinguish
@@ -120,7 +133,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
// (regardless of the evaluation of the condition).
// to support (1) use the infos of both branches originating from the condition for computing the nullStatus:
- computeNullStatus(trueFlowInfo, falseFlowInfo);
+ computeNullStatus(preComputedTrueNullStatus, trueFlowInfo, falseFlowInfo, flowContext);
// to support (2) we split the true/false branches according to their inner structure. Consider this:
// if (b ? false : (true && (v = false))) return v; -- ok
@@ -163,11 +176,13 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
return mergedInfo;
}
- private void computeNullStatus(FlowInfo trueBranchInfo, FlowInfo falseBranchInfo) {
+ private void computeNullStatus(int ifTrueNullStatus, FlowInfo trueBranchInfo, FlowInfo falseBranchInfo, FlowContext flowContext) {
// given that the condition cannot be optimized to a constant
// we now merge the nullStatus from both branches:
- int ifTrueNullStatus = this.valueIfTrue.nullStatus(trueBranchInfo);
- int ifFalseNullStatus = this.valueIfFalse.nullStatus(falseBranchInfo);
+ if (ifTrueNullStatus == -1) { // has this status been pre-computed?
+ ifTrueNullStatus = this.valueIfTrue.nullStatus(trueBranchInfo, flowContext);
+ }
+ int ifFalseNullStatus = this.valueIfFalse.nullStatus(falseBranchInfo, flowContext);
if (ifTrueNullStatus == ifFalseNullStatus) {
this.nullStatus = ifTrueNullStatus;
@@ -384,7 +399,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
codeStream.recordPositionsFrom(pc, this.sourceEnd);
}
- public int nullStatus(FlowInfo flowInfo) {
+ public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
return this.nullStatus;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
index 63a797116..e56b23c7c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
@@ -16,6 +16,8 @@
* bug 361407 - Resource leak warning when resource is assigned to a field outside of constructor
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 383690 - [compiler] location of error re uninitialized final field should be aligned
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -275,12 +277,16 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial
// propagate to statements
if (this.statements != null) {
+ boolean enableSyntacticNullAnalysisForFields = this.scope.compilerOptions().enableSyntacticNullAnalysisForFields;
int complaintLevel = (nonStaticFieldInfoReachMode & FlowInfo.UNREACHABLE) == 0 ? Statement.NOT_COMPLAINED : Statement.COMPLAINED_FAKE_REACHABLE;
for (int i = 0, count = this.statements.length; i < count; i++) {
Statement stat = this.statements[i];
if ((complaintLevel = stat.complainIfUnreachable(flowInfo, this.scope, complaintLevel, true)) < Statement.COMPLAINED_UNREACHABLE) {
flowInfo = stat.analyseCode(this.scope, constructorContext, flowInfo);
}
+ if (enableSyntacticNullAnalysisForFields) {
+ constructorContext.expireNullCheckedFieldInfo();
+ }
}
}
// check for missing returning path
@@ -293,7 +299,7 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=235781
// flowInfo.setReachMode(initialReachMode);
- // check missing blank final field initializations
+ // check missing blank final field initializations (plus @NonNull)
if ((this.constructorCall != null)
//{ObjectTeams: no checking for some more cases:
// don't need to check tsuper ctors:
@@ -305,15 +311,21 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial
flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn);
FieldBinding[] fields = this.binding.declaringClass.fields();
for (int i = 0, count = fields.length; i < count; i++) {
- FieldBinding field;
- if ((!(field = fields[i]).isStatic())
- && field.isFinal()
- && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
- this.scope.problemReporter().uninitializedBlankFinalField(
- field,
- ((this.bits & ASTNode.IsDefaultConstructor) != 0)
- ? (ASTNode) this.scope.referenceType().declarationOf(field.original())
- : this);
+ FieldBinding field = fields[i];
+ if (!field.isStatic() && !flowInfo.isDefinitelyAssigned(field)) {
+ if (field.isFinal()) {
+ this.scope.problemReporter().uninitializedBlankFinalField(
+ field,
+ ((this.bits & ASTNode.IsDefaultConstructor) != 0)
+ ? (ASTNode) this.scope.referenceType().declarationOf(field.original())
+ : this);
+ } else if (field.isNonNull()) {
+ this.scope.problemReporter().uninitializedNonNullField(
+ field,
+ ((this.bits & ASTNode.IsDefaultConstructor) != 0)
+ ? (ASTNode) this.scope.referenceType().declarationOf(field.original())
+ : this);
+ }
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
index ef9274bc1..d3c33036f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
@@ -7,7 +7,10 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking
+ * Stephan Herrmann - Contributions for
+ * bug 186342 - [compiler][null] Using annotations for null checking
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -24,28 +27,67 @@ public class EqualExpression extends BinaryExpression {
super(left,right,operator);
}
private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
- int rightStatus = this.right.nullStatus(flowInfo);
- int leftStatus = this.left.nullStatus(flowInfo);
- // check if either is a method annotated @NonNull and compared to null:
+
+ // collect null status of child nodes:
+ int rightStatus = this.right.nullStatus(flowInfo, flowContext);
+ int leftStatus = this.left.nullStatus(flowInfo, flowContext);
+
+ boolean leftNonNullChecked = false;
+ boolean rightNonNullChecked = false;
+
+ // check if either is a non-local expression known to be nonnull and compared to null, candidates are
+ // - method/field annotated @NonNull
+ // - allocation expression, some literals, this reference (see inside expressionNonNullComparison(..))
+ // these checks do not leverage the flowInfo.
+ boolean checkEquality = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
if (leftStatus == FlowInfo.NON_NULL && rightStatus == FlowInfo.NULL) {
- if (this.left instanceof MessageSend) {
- scope.problemReporter().messageSendRedundantCheckOnNonNull(((MessageSend) this.left).binding, this.left);
- }
- // TODO: handle all kinds of expressions (cf. also https://bugs.eclipse.org/364326)
+ leftNonNullChecked = scope.problemReporter().expressionNonNullComparison(this.left, checkEquality);
} else if (leftStatus == FlowInfo.NULL && rightStatus == FlowInfo.NON_NULL) {
- if (this.right instanceof MessageSend) {
- scope.problemReporter().messageSendRedundantCheckOnNonNull(((MessageSend) this.right).binding, this.right);
+ rightNonNullChecked = scope.problemReporter().expressionNonNullComparison(this.right, checkEquality);
+ }
+
+ // perform flowInfo-based checks for variables and record info for syntactic null analysis for fields:
+ if (!leftNonNullChecked) {
+ LocalVariableBinding local = this.left.localVariableBinding();
+ if (local != null) {
+ if ((local.type.tagBits & TagBits.IsBaseType) == 0) {
+ checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, rightStatus, this.left);
+ }
+ } else if (this.left instanceof Reference
+ && ((!checkEquality && rightStatus == FlowInfo.NULL) || (checkEquality && rightStatus == FlowInfo.NON_NULL))
+ && scope.compilerOptions().enableSyntacticNullAnalysisForFields)
+ {
+ FieldBinding field = ((Reference)this.left).lastFieldBinding();
+ if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
+ flowContext.recordNullCheckedFieldReference((Reference) this.left, 1);
+ }
}
- // TODO: handle all kinds of expressions (cf. also https://bugs.eclipse.org/364326)
}
-
- LocalVariableBinding local = this.left.localVariableBinding();
- if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
- checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, rightStatus, this.left);
+ if (!rightNonNullChecked) {
+ LocalVariableBinding local = this.right.localVariableBinding();
+ if (local != null) {
+ if ((local.type.tagBits & TagBits.IsBaseType) == 0) {
+ checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, leftStatus, this.right);
+ }
+ } else if (this.right instanceof Reference
+ && ((!checkEquality && leftStatus == FlowInfo.NULL) || (checkEquality && leftStatus == FlowInfo.NON_NULL))
+ && scope.compilerOptions().enableSyntacticNullAnalysisForFields)
+ {
+ FieldBinding field = ((Reference)this.right).lastFieldBinding();
+ if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
+ flowContext.recordNullCheckedFieldReference((Reference) this.right, 1);
+ }
+ }
}
- local = this.right.localVariableBinding();
- if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
- checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, leftStatus, this.right);
+
+ // handle reachability:
+ if (leftNonNullChecked || rightNonNullChecked) {
+ // above checks have not propagated unreachable into the corresponding branch, do it now:
+ if (checkEquality) {
+ initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
+ } else {
+ initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
+ }
}
}
private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
index c9ea3768b..25b31e126 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
@@ -10,9 +10,11 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
* bug 292478 - Report potentially null across variable assignment
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -45,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement;
import org.eclipse.jdt.internal.compiler.util.Messages;
@@ -704,14 +707,21 @@ boolean handledByGeneratedMethod(Scope scope, TypeBinding castType, TypeBinding
{ return false; }
// SH}
/**
- * Check the local variable of this expression, if any, against potential NPEs
- * given a flow context and an upstream flow info. If so, report the risk to
- * the context. Marks the local as checked, which affects the flow info.
+ * Check this expression against potential NPEs, which may occur:
+ * <ul>
+ * <li>if the expression is the receiver in a field access, qualified allocation, array reference or message send
+ * incl. implicit message sends like it happens for the collection in a foreach statement.</li>
+ * <li>if the expression is subject to unboxing</li>
+ * <li>if the expression is the exception in a throw statement</li>
+ * </ul>
+ * If a risk of NPE is detected report it to the context.
+ * If the expression denotes a local variable, mark it as checked, which affects the flow info.
* @param scope the scope of the analysis
* @param flowContext the current flow context
* @param flowInfo the upstream flow info; caveat: may get modified
+ * @return could this expression be checked by the current implementation?
*/
-public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
LocalVariableBinding local = localVariableBinding();
if (local != null &&
(local.type.tagBits & TagBits.IsBaseType) == 0) {
@@ -728,7 +738,9 @@ public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInf
if (flowContext.initsOnFinally != null) {
flowContext.markFinallyNullStatus(local, FlowInfo.NON_NULL);
}
+ return true;
}
+ return false; // not checked
}
public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
@@ -1050,11 +1062,11 @@ public void markAsNonNull() {
this.bits |= ASTNode.IsNonNull;
}
-public int nullStatus(FlowInfo flowInfo) {
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
if (/* (this.bits & IsNonNull) != 0 || */
this.constant != null && this.constant != Constant.NotAConstant)
- return FlowInfo.NON_NULL; // constant expression cannot be null
+ return FlowInfo.NON_NULL; // constant expression cannot be null
LocalVariableBinding local = localVariableBinding();
if (local != null)
@@ -1297,4 +1309,13 @@ public void traverse(ASTVisitor visitor, BlockScope scope) {
public void traverse(ASTVisitor visitor, ClassScope scope) {
// nothing to do
}
+
+/**
+ * Used on the lhs of an assignment for detecting null spec violation.
+ * If this expression represents a null-annotated variable return the variable binding,
+ * otherwise null.
+*/
+public VariableBinding nullAnnotatedVariableBinding() {
+ return null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
index 00d6a113d..dac84ffdf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
@@ -10,6 +10,9 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -112,6 +115,16 @@ public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowCon
.unconditionalInits();
flowInfo.markAsDefinitelyAssigned(this.binding);
}
+ if (this.initialization != null) {
+ if (this.binding.isNonNull()) {
+ int nullStatus = this.initialization.nullStatus(flowInfo, flowContext);
+ // check against annotation @NonNull:
+ if (nullStatus != FlowInfo.NON_NULL) {
+ char[][] annotationName = initializationScope.environment().getNonNullAnnotationName();
+ initializationScope.problemReporter().nullityMismatch(this.initialization, this.initialization.resolvedType, this.binding.type, nullStatus, annotationName);
+ }
+ }
+ }
return flowInfo;
}
@@ -270,7 +283,7 @@ public void resolve(MethodScope initializationScope) {
if (fieldType != initializationType) // must call before computeConversion() and typeMismatchError()
initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType);
if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType)
- || initializationType.isCompatibleWith(fieldType)) {
+ || initializationType.isCompatibleWith(fieldType, classScope)) {
this.initialization.computeConversion(initializationScope, fieldType, initializationType);
if (initializationType.needsUncheckedConversion(fieldType)) {
initializationScope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, fieldType);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
index 70fe09a84..506a1a1ba 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
@@ -8,9 +8,12 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * bug 185682 - Increment/decrement operators mark local variables as read
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -27,6 +30,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
+import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
@@ -41,6 +45,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
@@ -154,6 +159,14 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte
// assigning a final field outside an initializer or constructor or wrong reference
currentScope.problemReporter().cannotAssignToFinalField(this.binding, this);
}
+ } else if (this.binding.isNonNull()) {
+ // in a context where it can be assigned?
+ if ( !isCompound
+ && this.receiver.isThis()
+ && !(this.receiver instanceof QualifiedThisReference)
+ && ((this.receiver.bits & ASTNode.ParenthesizedMASK) == 0)) { // (this).x is forbidden
+ flowInfo.markAsDefinitelyAssigned(this.binding);
+ }
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
if (!this.binding.isStatic()) {
@@ -196,6 +209,13 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ if (flowContext.isNullcheckedFieldAccess(this)) {
+ return true; // enough seen
+ }
+ return checkNullableFieldDereference(scope, this.binding, this.nameSourcePosition);
+}
+
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
*/
@@ -478,6 +498,58 @@ public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream
public TypeBinding[] genericTypeArguments() {
return null;
}
+
+public boolean isEquivalent(Reference reference) {
+ // only consider field references relative to "this":
+ if (this.receiver.isThis() && !(this.receiver instanceof QualifiedThisReference)) {
+ // current is a simple "this.f1"
+ char[] otherToken = null;
+ // matching 'reference' could be "f1" or "this.f1":
+ if (reference instanceof SingleNameReference) {
+ otherToken = ((SingleNameReference) reference).token;
+ } else if (reference instanceof FieldReference) {
+ FieldReference fr = (FieldReference) reference;
+ if (fr.receiver.isThis() && !(fr.receiver instanceof QualifiedThisReference)) {
+ otherToken = fr.token;
+ }
+ }
+ return otherToken != null && CharOperation.equals(this.token, otherToken);
+ } else {
+ // search deeper for "this" inside:
+ char[][] thisTokens = getThisFieldTokens(1);
+ if (thisTokens == null) {
+ return false;
+ }
+ // other can be "this.f1.f2", too, or "f1.f2":
+ char[][] otherTokens = null;
+ if (reference instanceof FieldReference) {
+ otherTokens = ((FieldReference) reference).getThisFieldTokens(1);
+ } else if (reference instanceof QualifiedNameReference) {
+ if (((QualifiedNameReference)reference).binding instanceof LocalVariableBinding)
+ return false; // initial variable mismatch: local (from f1.f2) vs. field (from this.f1.f2)
+ otherTokens = ((QualifiedNameReference) reference).tokens;
+ }
+ return CharOperation.equals(thisTokens, otherTokens);
+ }
+}
+
+private char[][] getThisFieldTokens(int nestingCount) {
+ char[][] result = null;
+ if (this.receiver.isThis() && ! (this.receiver instanceof QualifiedThisReference)) {
+ // found an inner-most this-reference, start building the token array:
+ result = new char[nestingCount][];
+ // fill it front to tail while traveling back out:
+ result[0] = this.token;
+ } else if (this.receiver instanceof FieldReference) {
+ result = ((FieldReference)this.receiver).getThisFieldTokens(nestingCount+1);
+ if (result != null) {
+ // front to tail: outermost is last:
+ result[result.length-nestingCount] = this.token;
+ }
+ }
+ return result;
+}
+
public boolean isSuperAccess() {
return this.receiver.isSuper();
}
@@ -486,6 +558,10 @@ public boolean isTypeAccess() {
return this.receiver != null && this.receiver.isTypeReference();
}
+public FieldBinding lastFieldBinding() {
+ return this.binding;
+}
+
/*
* No need to emulate access to protected fields since not implicitly accessed
*/
@@ -573,9 +649,6 @@ private boolean isRemoteRoleFieldAccess() {
&& this.binding.declaringClass.isRole();
}
// SH}
-public int nullStatus(FlowInfo flowInfo) {
- return FlowInfo.UNKNOWN;
-}
public Constant optimizedBooleanConstant() {
switch (this.resolvedType.id) {
@@ -832,4 +905,12 @@ public void traverse(ASTVisitor visitor, BlockScope scope) {
}
visitor.endVisit(this, scope);
}
+
+public VariableBinding nullAnnotatedVariableBinding() {
+ if (this.binding != null
+ && ((this.binding.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0)) {
+ return this.binding;
+ }
+ return null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
index 50632b616..cb28d425f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
@@ -13,6 +13,7 @@
* bug 370930 - NonNull annotation not considered for enhanced for loops
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 393719 - [compiler] inconsistent warnings on iteration variables
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -443,7 +444,7 @@ public class ForeachStatement extends Statement {
&& !this.scope.isBoxingCompatibleWith(this.collectionElementType, elementType)) {
this.scope.problemReporter().notCompatibleTypesErrorInForeach(this.collection, this.collectionElementType, elementType);
} else if (this.collectionElementType.needsUncheckedConversion(elementType)) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321085
- this.scope.problemReporter().unsafeTypeConversion(this.collection, collectionType, upperScope.createArrayType(elementType, 1));
+ this.scope.problemReporter().unsafeElementTypeConversion(this.collection, this.collectionElementType, elementType);
}
// :giro
if (Config.getLoweringRequired())
@@ -534,6 +535,8 @@ public class ForeachStatement extends Statement {
if (!this.collectionElementType.isCompatibleWith(elementType)
&& !this.scope.isBoxingCompatibleWith(this.collectionElementType, elementType)) {
this.scope.problemReporter().notCompatibleTypesErrorInForeach(this.collection, this.collectionElementType, elementType);
+ } else if (this.collectionElementType.needsUncheckedConversion(elementType)) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=393719
+ this.scope.problemReporter().unsafeElementTypeConversion(this.collection, this.collectionElementType, elementType);
}
// :giro
if (Config.getLoweringRequired())
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java
index 858ed848c..60de061ff 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java
@@ -11,6 +11,7 @@
* bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -104,6 +105,8 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
}
thenFlowInfo = this.thenStatement.analyseCode(currentScope, flowContext, thenFlowInfo);
}
+ // any null check from the condition is now expired
+ flowContext.expireNullCheckedFieldInfo();
// code gen: optimizing the jump around the ELSE part
if ((thenFlowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) {
this.bits |= ASTNode.ThenExit;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
index 045210d7c..7054549fd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
@@ -10,6 +10,8 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -74,6 +76,12 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
// no impact upon enclosing try context
return FlowInfo.conditional(initsWhenTrue, flowInfo.copy());
}
+ if (this.expression instanceof Reference && currentScope.compilerOptions().enableSyntacticNullAnalysisForFields) {
+ FieldBinding field = ((Reference)this.expression).lastFieldBinding();
+ if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
+ flowContext.recordNullCheckedFieldReference((Reference) this.expression, 1);
+ }
+ }
return this.expression.analyseCode(currentScope, flowContext, flowInfo).
unconditionalInits();
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
index 51d7a75f3..96edbc149 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -660,6 +660,11 @@ public class Javadoc extends ASTNode {
TypeBinding paramBindind = param.internalResolveType(scope);
if (paramBindind != null && paramBindind.isValidBinding()) {
if (paramBindind.isTypeVariable()) {
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=397888
+ if (scope.compilerOptions().reportUnusedParameterIncludeDocCommentReference) {
+ TypeVariableBinding typeVariableBinding = (TypeVariableBinding) paramBindind;
+ typeVariableBinding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
+ }
// Verify duplicated tags
boolean duplicate = false;
for (int j = 0; j < i && !duplicate; j++) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
index 3e5af5e54..b21f46a1f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
@@ -21,6 +21,8 @@
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -159,7 +161,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
else
FakedTrackingVariable.cleanUpAfterAssignment(currentScope, Binding.LOCAL, this.initialization);
- int nullStatus = this.initialization.nullStatus(flowInfo);
+ int nullStatus = this.initialization.nullStatus(flowInfo, flowContext);
if (!flowInfo.isDefinitelyAssigned(this.binding)){// for local variable debug attributes
this.bits |= FirstAssignmentToLocal;
} else {
@@ -328,7 +330,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
if (variableType != initializationType) // must call before computeConversion() and typeMismatchError()
scope.compilationUnitScope().recordTypeConversion(variableType, initializationType);
if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, variableType)
- || initializationType.isCompatibleWith(variableType)) {
+ || initializationType.isCompatibleWith(variableType, scope)) {
this.initialization.computeConversion(scope, variableType, initializationType);
if (initializationType.needsUncheckedConversion(variableType)) {
scope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, variableType);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 9c3209ad5..4f2ef2b27 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -24,6 +24,8 @@
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
* bug 381445 - [compiler][resource] Can the resource leak check be made aware of Closeables.closeQuietly?
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -313,10 +315,11 @@ protected FlowInfo checkBaseCallsIfSuper(BlockScope currentScope, FlowInfo flowI
return flowInfo;
}
// SH}
-public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
- super.checkNPE(scope, flowContext, flowInfo);
- if ((nullStatus(flowInfo) & FlowInfo.POTENTIALLY_NULL) != 0)
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ // message send as a receiver
+ if ((nullStatus(flowInfo, flowContext) & FlowInfo.POTENTIALLY_NULL) != 0)
scope.problemReporter().messageSendPotentialNullReference(this.binding, this);
+ return true; // done all possible checking
}
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
@@ -547,7 +550,7 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo f
}
// SH}
}
-public int nullStatus(FlowInfo flowInfo) {
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
if (this.binding.isValidBinding()) {
// try to retrieve null status of this message send from an annotation of the called method:
long tagBits = this.binding.tagBits;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
index 828b79f2c..885caa76e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
@@ -15,6 +15,7 @@
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -163,12 +164,16 @@ public class MethodDeclaration extends AbstractMethodDeclaration {
}
// propagate to statements
if (this.statements != null) {
+ boolean enableSyntacticNullAnalysisForFields = this.scope.compilerOptions().enableSyntacticNullAnalysisForFields;
int complaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) == 0 ? Statement.NOT_COMPLAINED : Statement.COMPLAINED_FAKE_REACHABLE;
for (int i = 0, count = this.statements.length; i < count; i++) {
Statement stat = this.statements[i];
if ((complaintLevel = stat.complainIfUnreachable(flowInfo, this.scope, complaintLevel, true)) < Statement.COMPLAINED_UNREACHABLE) {
flowInfo = stat.analyseCode(this.scope, methodContext, flowInfo);
}
+ if (enableSyntacticNullAnalysisForFields) {
+ methodContext.expireNullCheckedFieldInfo();
+ }
}
} else {
// method with empty body should not be flagged as static.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NameReference.java
index 8ee585d40..8a8aaea95 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NameReference.java
@@ -10,6 +10,8 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -69,12 +71,23 @@ public NameReference() {
this.bits |= Binding.TYPE | Binding.VARIABLE; // restrictiveFlag
}
+/**
+ * Use this method only when sure that the current reference is <strong>not</strong>
+ * a chain of several fields (QualifiedNameReference with more than one field).
+ * Otherwise use {@link #lastFieldBinding()}.
+ */
public FieldBinding fieldBinding() {
//this method should be sent ONLY after a check against isFieldReference()
//check its use doing senders.........
return (FieldBinding) this.binding ;
}
+public FieldBinding lastFieldBinding() {
+ if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD)
+ return fieldBinding(); // most subclasses only refer to one field anyway
+ return null;
+}
+
public boolean isSuperAccess() {
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullLiteral.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullLiteral.java
index aa77770d4..5e5bbbd5f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullLiteral.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullLiteral.java
@@ -7,11 +7,14 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.*;
+import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
@@ -49,7 +52,7 @@ public class NullLiteral extends MagicLiteral {
return TypeBinding.NULL;
}
- public int nullStatus(FlowInfo flowInfo) {
+ public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
return FlowInfo.NULL;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java
index 443391d5c..111714a5d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java
@@ -7,7 +7,9 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann - Contribution for bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
+ * Stephan Herrmann - Contributions for
+ * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -41,13 +43,16 @@ public class OR_OR_Expression extends BinaryExpression {
// need to be careful of scenario:
// (x || y) || !z, if passing the left info to the right, it would be swapped by the !
FlowInfo mergedInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
+ flowContext.expireNullCheckedFieldInfo();
mergedInfo = this.right.analyseCode(currentScope, flowContext, mergedInfo);
+ flowContext.expireNullCheckedFieldInfo();
this.mergedInitStateIndex =
currentScope.methodScope().recordInitializationStates(mergedInfo);
return mergedInfo;
}
FlowInfo leftInfo = this.left.analyseCode(currentScope, flowContext, flowInfo);
+ flowContext.expireNullCheckedFieldInfo();
// need to be careful of scenario:
// (x || y) || !z, if passing the left info to the right, it would be swapped by the !
@@ -63,6 +68,7 @@ public class OR_OR_Expression extends BinaryExpression {
}
}
rightInfo = this.right.analyseCode(currentScope, flowContext, rightInfo);
+ flowContext.expireNullCheckedFieldInfo();
if ((this.left.implicitConversion & TypeIds.UNBOXING) != 0) {
this.left.checkNPE(currentScope, flowContext, flowInfo);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OperatorExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OperatorExpression.java
index f43174d10..8cd458422 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OperatorExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OperatorExpression.java
@@ -8,9 +8,12 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Perry James - nullStatus method improvement (165346)
+ * Stephan Herrmann - Contribution for
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
+import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.util.Util;
@@ -1556,7 +1559,7 @@ public abstract class OperatorExpression extends Expression implements OperatorI
return "unknown operator"; //$NON-NLS-1$
}
- public int nullStatus(FlowInfo flowInfo) {
+ public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
return FlowInfo.NON_NULL;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
index 3d8cd1135..54ba6a86e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
@@ -16,6 +16,8 @@
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -140,7 +142,7 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte
localBinding.useFlag = LocalVariableBinding.FAKE_USED;
}
if (needValue) {
- checkNPE(currentScope, flowContext, flowInfo, true);
+ checkInternalNPE(currentScope, flowContext, flowInfo, true);
}
}
@@ -201,6 +203,7 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte
}
}
}
+ // note: not covering def.assign for @NonNull: QNR cannot provably refer to a variable of the current object
manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, -1 /*write-access*/, flowInfo);
return flowInfo;
@@ -246,9 +249,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
localBinding.useFlag = LocalVariableBinding.FAKE_USED;
}
- if (needValue) {
- checkNPE(currentScope, flowContext, flowInfo, true);
- }
+ }
+ if (needValue) {
+ checkInternalNPE(currentScope, flowContext, flowInfo, true);
}
if (needValue) {
manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
@@ -265,9 +268,8 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
-public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, boolean checkString) {
- // cannot override localVariableBinding because this would project o.m onto o when
- // analyzing assignments
+/* check if any dot in this QNR may trigger an NPE. */
+private void checkInternalNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, boolean checkString) {
if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.LOCAL) {
LocalVariableBinding local = (LocalVariableBinding) this.binding;
if (local != null &&
@@ -284,6 +286,38 @@ public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInf
}
}
}
+ if (this.otherBindings != null) {
+ if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD) {
+ // is the first field dereferenced annotated Nullable? If so, report immediately
+ checkNullableFieldDereference(scope, (FieldBinding) this.binding, this.sourcePositions[0]);
+ }
+ // look for annotated fields, they do not depend on flow context -> check immediately:
+ int length = this.otherBindings.length - 1; // don't check the last binding
+ for (int i = 0; i < length; i++) {
+ checkNullableFieldDereference(scope, this.otherBindings[i], this.sourcePositions[i+1]);
+ }
+ }
+}
+
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ if (super.checkNPE(scope, flowContext, flowInfo)) {
+ return true;
+ }
+ FieldBinding fieldBinding = null;
+ long position = 0L;
+ if (this.otherBindings == null) {
+ if ((this.bits & RestrictiveFlagMASK) == Binding.FIELD) {
+ fieldBinding = (FieldBinding) this.binding;
+ position = this.sourcePositions[0];
+ }
+ } else {
+ fieldBinding = this.otherBindings[this.otherBindings.length - 1];
+ position = this.sourcePositions[this.sourcePositions.length - 1];
+ }
+ if (fieldBinding != null) {
+ return checkNullableFieldDereference(scope, fieldBinding, position);
+ }
+ return false;
}
/**
@@ -905,12 +939,45 @@ private void accessAsCalloutToField(ReferenceBinding enclosingReceiver, FieldBin
setSyntheticAccessor(baseclassField, idx, new SyntheticMethodBinding(fakedAccessorBinding, SyntheticMethodBinding.InferredCalloutToField));
}
// SH}
+
+public boolean isEquivalent(Reference reference) {
+ if (reference instanceof FieldReference) {
+ return reference.isEquivalent(this); // comparison FR <-> QNR is implemented only once
+ }
+ if (!(reference instanceof QualifiedNameReference)) return false;
+ // straight-forward test of equality of two QNRs:
+ QualifiedNameReference qualifiedReference = (QualifiedNameReference) reference;
+ if (this.tokens.length != qualifiedReference.tokens.length) return false;
+ if (this.binding != qualifiedReference.binding) return false;
+ if (this.otherBindings != null) {
+ if (qualifiedReference.otherBindings == null) return false;
+ int len = this.otherBindings.length;
+ if (len != qualifiedReference.otherBindings.length) return false;
+ for (int i=0; i<len; i++) {
+ if (this.otherBindings[i] != qualifiedReference.otherBindings[i]) return false;
+ }
+ } else if (qualifiedReference.otherBindings != null) {
+ return false;
+ }
+ return true;
+}
+
public boolean isFieldAccess() {
if (this.otherBindings != null) {
return true;
}
return (this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD;
}
+
+public FieldBinding lastFieldBinding() {
+ if (this.otherBindings != null) {
+ return this.otherBindings[this.otherBindings.length - 1];
+ } else if (this.binding != null && (this.bits & RestrictiveFlagMASK) == Binding.FIELD) {
+ return (FieldBinding) this.binding;
+ }
+ return null;
+}
+
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
//If inlinable field, forget the access emulation, the code gen will directly target it
if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) {
@@ -1014,10 +1081,6 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FieldBindi
}
}
-public int nullStatus(FlowInfo flowInfo) {
- return FlowInfo.UNKNOWN;
-}
-
public Constant optimizedBooleanConstant() {
switch (this.resolvedType.id) {
case T_boolean :
@@ -1299,4 +1362,19 @@ public void traverse(ASTVisitor visitor, ClassScope scope) {
public String unboundReferenceErrorName() {
return new String(this.tokens[0]);
}
+
+public VariableBinding nullAnnotatedVariableBinding() {
+ if (this.binding != null && isFieldAccess()) {
+ FieldBinding fieldBinding;
+ if (this.otherBindings == null) {
+ fieldBinding = (FieldBinding) this.binding;
+ } else {
+ fieldBinding = this.otherBindings[this.otherBindings.length - 1];
+ }
+ if (fieldBinding.isNullable() || fieldBinding.isNonNull()) {
+ return fieldBinding;
+ }
+ }
+ return null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
index 72f3a744c..5714216e9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
@@ -7,9 +7,12 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * bug 185682 - Increment/decrement operators mark local variables as read
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -26,6 +29,7 @@ import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
@@ -59,6 +63,21 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ if (flowContext.isNullcheckedFieldAccess(this)) {
+ return true; // enough seen
+ }
+ return super.checkNPE(scope, flowContext, flowInfo);
+}
+
+protected boolean checkNullableFieldDereference(Scope scope, FieldBinding field, long sourcePosition) {
+ if ((field.tagBits & TagBits.AnnotationNullable) != 0) {
+ scope.problemReporter().nullableFieldDereference(field, sourcePosition);
+ return true;
+ }
+ return false;
+}
+
public FieldBinding fieldBinding() {
//this method should be sent one FIELD-tagged references
// (ref.bits & BindingIds.FIELD != 0)()
@@ -113,6 +132,34 @@ public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStr
public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired);
+/**
+ * Is the given reference equivalent to the receiver,
+ * meaning that both denote the same path of field reads?
+ * Used from {@link FlowContext#isNullcheckedFieldAccess(Reference)}.
+ */
+public boolean isEquivalent(Reference reference) {
+ return false;
+}
+
+public FieldBinding lastFieldBinding() {
+ // override to answer the field designated by the entire reference
+ // (as opposed to fieldBinding() which answers the first field in a QNR)
+ return null;
+}
+
+public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
+ FieldBinding fieldBinding = lastFieldBinding();
+ if (fieldBinding != null) {
+ if (fieldBinding.isNonNull() || flowContext.isNullcheckedFieldAccess(this)) {
+ return FlowInfo.NON_NULL;
+ } else if (fieldBinding.isNullable()) {
+ return FlowInfo.POTENTIALLY_NULL;
+ }
+ return FlowInfo.UNKNOWN;
+ }
+ return super.nullStatus(flowInfo, flowContext);
+}
+
/* report if a private field 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 field. */
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
index 4c11965c9..d5e2ce9a4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
@@ -22,6 +22,7 @@
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -61,7 +62,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
this.expression.checkNPE(currentScope, flowContext, flowInfo);
}
if (flowInfo.reachMode() == FlowInfo.REACHABLE)
- checkAgainstNullAnnotation(currentScope, flowContext, this.expression.nullStatus(flowInfo));
+ checkAgainstNullAnnotation(currentScope, flowContext, this.expression.nullStatus(flowInfo, flowContext));
if (currentScope.compilerOptions().analyseResourceLeaks) {
FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression, flowInfo, flowContext);
if (trackingVariable != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
index be997933d..2afd2bfaf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
@@ -4,14 +4,16 @@
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
- * $Id: SingleNameReference.java 23404 2010-02-03 14:10:22Z stephan $
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 292478 - Report potentially null across variable assignment,
- * Contribution for bug 185682 - Increment/decrement operators mark local variables as read
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * bug 292478 - Report potentially null across variable assignment,
+ * bug 185682 - Increment/decrement operators mark local variables as read
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -61,7 +63,6 @@ import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
*
* What: wrap role type in resolveType().
*
- * @version $Id: SingleNameReference.java 23404 2010-02-03 14:10:22Z stephan $
*/
public class SingleNameReference extends NameReference implements OperatorIds {
@@ -139,6 +140,9 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte
} else {
currentScope.problemReporter().cannotAssignToFinalField(fieldBinding, this);
}
+ } else if (!isCompound && fieldBinding.isNonNull()) {
+ // record assignment for detecting uninitialized non-null fields:
+ flowInfo.markAsDefinitelyAssigned(fieldBinding);
}
if (!fieldBinding.isStatic()) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
@@ -264,6 +268,17 @@ public TypeBinding checkFieldAccess(BlockScope scope) {
}
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ if (!super.checkNPE(scope, flowContext, flowInfo)) {
+ VariableBinding var = nullAnnotatedVariableBinding();
+ if (var instanceof FieldBinding) {
+ checkNullableFieldDereference(scope, (FieldBinding) var, ((long)this.sourceStart<<32)+this.sourceEnd);
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
*/
@@ -825,6 +840,19 @@ public TypeBinding[] genericTypeArguments() {
return null;
}
+public boolean isEquivalent(Reference reference) {
+ char[] otherToken = null;
+ if (reference instanceof SingleNameReference) {
+ otherToken = ((SingleNameReference) reference).token;
+ } else if (reference instanceof FieldReference) {
+ // test for comparison "f1" vs. "this.f1":
+ FieldReference fr = (FieldReference) reference;
+ if (fr.receiver.isThis() && !(fr.receiver instanceof QualifiedThisReference))
+ otherToken = fr.token;
+ }
+ return otherToken != null && CharOperation.equals(this.token, otherToken);
+}
+
/**
* Returns the local variable referenced by this node. Can be a direct reference (SingleNameReference)
* or thru a cast expression etc...
@@ -839,6 +867,16 @@ public LocalVariableBinding localVariableBinding() {
return null;
}
+public VariableBinding nullAnnotatedVariableBinding() {
+ switch (this.bits & ASTNode.RestrictiveFlagMASK) {
+ case Binding.FIELD : // reading a field
+ case Binding.LOCAL : // reading a local variable
+ if ((((VariableBinding)this.binding).tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0)
+ return (VariableBinding) this.binding;
+ }
+ return null;
+}
+
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
//If inlinable field, forget the access emulation, the code gen will directly target it
if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) {
@@ -901,22 +939,7 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo f
}
}
-public int nullStatus(FlowInfo flowInfo) {
- if (this.constant != null && this.constant != Constant.NotAConstant) {
- return FlowInfo.NON_NULL; // constant expression cannot be null
- }
- switch (this.bits & ASTNode.RestrictiveFlagMASK) {
- case Binding.FIELD : // reading a field
- return FlowInfo.UNKNOWN;
- case Binding.LOCAL : // reading a local variable
- LocalVariableBinding local = (LocalVariableBinding) this.binding;
- if (local != null)
- return flowInfo.nullStatus(local);
- }
- return FlowInfo.NON_NULL; // never get there
-}
-
- /**
+/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope)
*/
public TypeBinding postConversionType(Scope scope) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
index 9f79f10e6..588c2e5ec 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
@@ -18,6 +18,8 @@
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 370930 - NonNull annotation not considered for enhanced for loops
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -103,7 +105,7 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext
if (methodBinding.parameterNonNullness[i] == Boolean.TRUE) {
TypeBinding expectedType = methodBinding.parameters[i];
Expression argument = arguments[i];
- int nullStatus = argument.nullStatus(flowInfo); // slight loss of precision: should also use the null info from the receiver.
+ int nullStatus = argument.nullStatus(flowInfo, flowContext); // slight loss of precision: should also use the null info from the receiver.
if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided
flowContext.recordNullityMismatch(currentScope, argument, argument.resolvedType, expectedType, nullStatus);
}
@@ -111,19 +113,17 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext
}
}
-/** Check null-ness of 'local' against a possible null annotation */
+/** Check null-ness of 'var' against a possible null annotation */
protected int checkAssignmentAgainstNullAnnotation(BlockScope currentScope, FlowContext flowContext,
- LocalVariableBinding local, int nullStatus, Expression expression, TypeBinding providedType)
+ VariableBinding var, int nullStatus, Expression expression, TypeBinding providedType)
{
- if (local != null) {
- if ((local.tagBits & TagBits.AnnotationNonNull) != 0
- && nullStatus != FlowInfo.NON_NULL) {
- flowContext.recordNullityMismatch(currentScope, expression, providedType, local.type, nullStatus);
- return FlowInfo.NON_NULL;
- } else if ((local.tagBits & TagBits.AnnotationNullable) != 0
- && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type?
- return FlowInfo.POTENTIALLY_NULL; // -> use more specific info from the annotation
- }
+ if ((var.tagBits & TagBits.AnnotationNonNull) != 0
+ && nullStatus != FlowInfo.NON_NULL) {
+ flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, nullStatus);
+ return FlowInfo.NON_NULL;
+ } else if ((var.tagBits & TagBits.AnnotationNullable) != 0
+ && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type?
+ return FlowInfo.POTENTIALLY_NULL; // -> use more specific info from the annotation
}
return nullStatus;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
index 670959b9b..26327c452 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
@@ -4,12 +4,14 @@
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
- * $Id: ThisReference.java 23404 2010-02-03 14:10:22Z stephan $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -25,8 +27,6 @@ import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
* OTDT changes:
*
* What: if it's not an ImplicitThis, wrap the type if its a role.
- *
- * @version $Id: ThisReference.java 23404 2010-02-03 14:10:22Z stephan $
*/
public class ThisReference extends Reference {
@@ -67,6 +67,10 @@ public class ThisReference extends Reference {
return true;
}
+ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ return true; // never problematic
+ }
+
/*
* @see Reference#generateAssignment(...)
*/
@@ -109,10 +113,6 @@ public class ThisReference extends Reference {
return true ;
}
- public int nullStatus(FlowInfo flowInfo) {
- return FlowInfo.NON_NULL;
- }
-
public StringBuffer printExpression(int indent, StringBuffer output){
if (isImplicitThis()) return output;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
index 06910c567..7a589e2af 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
@@ -778,7 +778,9 @@ public MethodBinding createDefaultConstructorWithBinding(MethodBinding inherited
sourceType); //declaringClass
constructor.binding.tagBits |= (inheritedConstructorBinding.tagBits & TagBits.HasMissingType);
constructor.binding.modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor;
- if (inheritedConstructorBinding.parameterNonNullness != null) { // this implies that annotation based null analysis is enabled
+ if (inheritedConstructorBinding.parameterNonNullness != null // this implies that annotation based null analysis is enabled
+ && argumentsLength > 0)
+ {
// copy nullness info from inherited constructor to the new constructor:
int len = inheritedConstructorBinding.parameterNonNullness.length;
System.arraycopy(inheritedConstructorBinding.parameterNonNullness, 0,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java
index 261bf8ee7..81b932311 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java
@@ -7,6 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -33,9 +35,11 @@ public FlowInfo analyseCode(
FlowInfo flowInfo) {
this.expression.checkNPE(currentScope, flowContext, flowInfo);
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
- return this.expression.
+ flowInfo = this.expression.
analyseCode(currentScope, flowContext, flowInfo).
asNegatedCondition();
+ flowContext.expireNullCheckedFieldInfo();
+ return flowInfo;
} else {
return this.expression.
analyseCode(currentScope, flowContext, flowInfo);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
index 299809719..d8e5dddeb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
@@ -13,6 +13,7 @@
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -70,6 +71,12 @@ public class FlowContext implements TypeConstants {
// array to store the provided and expected types from the potential error location (for display in error messages):
public TypeBinding[][] providedExpectedTypes = null;
+ // record field references known to be non-null
+ // this array will never shrink, only grow. reset happens by nulling the first cell
+ // adding elements after reset ensures that the valid part of the array is always null-terminated
+ private Reference[] nullCheckedFieldReferences = null;
+ private int timeToLiveForNullCheckInfo = -1;
+
public static final int DEFER_NULL_DIAGNOSTIC = 0x1;
public static final int PREEMPT_NULL_DIAGNOSTIC = 0x2;
/**
@@ -109,8 +116,73 @@ public FlowContext(FlowContext parent, ASTNode associatedNode) {
}
this.initsOnFinally = parent.initsOnFinally;
this.conditionalLevel = parent.conditionalLevel;
+ this.nullCheckedFieldReferences = parent.nullCheckedFieldReferences; // re-use list if there is one
+ }
+}
+
+/**
+ * Record that a reference to a field has been seen in a non-null state.
+ *
+ * @param reference Can be a SingleNameReference, a FieldReference or a QualifiedNameReference resolving to a field
+ * @param timeToLive control how many expire events are needed to expire this information
+ */
+public void recordNullCheckedFieldReference(Reference reference, int timeToLive) {
+ this.timeToLiveForNullCheckInfo = timeToLive;
+ if (this.nullCheckedFieldReferences == null) {
+ // first entry:
+ this.nullCheckedFieldReferences = new Reference[2];
+ this.nullCheckedFieldReferences[0] = reference;
+ } else {
+ int len = this.nullCheckedFieldReferences.length;
+ // insert into first empty slot:
+ for (int i=0; i<len; i++) {
+ if (this.nullCheckedFieldReferences[i] == null) {
+ this.nullCheckedFieldReferences[i] = reference;
+ if (i+1 < len) {
+ this.nullCheckedFieldReferences[i+1] = null; // lazily mark next as empty
+ }
+ return;
+ }
+ }
+ // grow array:
+ System.arraycopy(this.nullCheckedFieldReferences, 0, this.nullCheckedFieldReferences=new Reference[len+2], 0, len);
+ this.nullCheckedFieldReferences[len] = reference;
}
}
+/**
+ * Forget any information about fields that were previously known to be non-null.
+ *
+ * Will only cause any effect if CompilerOptions.enableSyntacticNullAnalysisForFields
+ * (implicitly by guards before calls to {@link #recordNullCheckedFieldReference(Reference, int)}).
+ */
+public void expireNullCheckedFieldInfo() {
+ if (this.nullCheckedFieldReferences != null) {
+ if (--this.timeToLiveForNullCheckInfo == 0) {
+ this.nullCheckedFieldReferences[0] = null; // lazily wipe
+ }
+ }
+}
+
+/**
+ * Is the given field reference equivalent to a reference that is freshly known to be non-null?
+ * Can only return true if CompilerOptions.enableSyntacticNullAnalysisForFields
+ * (implicitly by guards before calls to {@link #recordNullCheckedFieldReference(Reference, int)}).
+ */
+public boolean isNullcheckedFieldAccess(Reference reference) {
+ if (this.nullCheckedFieldReferences == null) // always null unless CompilerOptions.enableSyntacticNullAnalysisForFields
+ return false;
+ int len = this.nullCheckedFieldReferences.length;
+ for (int i=0; i<len; i++) {
+ Reference checked = this.nullCheckedFieldReferences[i];
+ if (checked == null) {
+ return false;
+ }
+ if (checked.isEquivalent(reference)) {
+ return true;
+ }
+ }
+ return false;
+}
public BranchLabel breakLabel() {
return null;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java
index 5020e8aea..5dcf13ac5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java
@@ -15,6 +15,7 @@
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 385626 - @NonNull fails across loop boundaries
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * bug 376263 - Bogus "Potential null pointer access" warning
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -148,6 +149,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn
addPotentialNullInfoFrom(this.innerFlowInfos[i]);
}
this.innerFlowContextsCount = 0;
+ FlowInfo upstreamCopy = this.upstreamNullFlowInfo.copy();
UnconditionalFlowInfo flowInfo = this.upstreamNullFlowInfo.
addPotentialNullInfoFrom(callerFlowInfo.unconditionalInitsWithoutSideEffect());
if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) {
@@ -277,8 +279,12 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn
default:
// never happens
}
- this.parent.recordUsingNullReference(scope, local, location,
- this.nullCheckTypes[i], flowInfo);
+ // https://bugs.eclipse.org/376263: avoid further deferring if the upstream info
+ // already has definite information (which might get lost for deferred checking).
+ if (!(this.nullCheckTypes[i] == MAY_NULL && upstreamCopy.isDefinitelyNonNull(local))) {
+ this.parent.recordUsingNullReference(scope, local, location,
+ this.nullCheckTypes[i], flowInfo);
+ }
}
}
else {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
index 67fdcff78..06a761e4e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -19,6 +19,8 @@
* bug 366063 - Compiler should not add synthetic @NonNull annotations
* bug 374605 - Unreasonable warning for enum-based switch statements
* bug 388281 - [compiler][null] inheritance of null annotations as an option
+ * bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
+ * bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.impl;
@@ -241,7 +243,9 @@ public class CompilerOptions {
static final char[][] DEFAULT_NONNULL_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNull".toCharArray()); //$NON-NLS-1$
static final char[][] DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNullByDefault".toCharArray()); //$NON-NLS-1$
public static final String OPTION_ReportMissingNonNullByDefaultAnnotation = "org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation"; //$NON-NLS-1$
+ public static final String OPTION_SyntacticNullAnalysisForFields = "org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields"; //$NON-NLS-1$
public static final String OPTION_InheritNullAnnotations = "org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations"; //$NON-NLS-1$
+ public static final String OPTION_ReportNonnullParameterAnnotationDropped = "org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped"; //$NON-NLS-1$
/**
* Possible values for configurable options
*/
@@ -355,6 +359,7 @@ public class CompilerOptions {
public static final int MissingNonNullByDefaultAnnotation = IrritantSet.GROUP2 | ASTNode.Bit15;
public static final int MissingDefaultCase = IrritantSet.GROUP2 | ASTNode.Bit16;
public static final int UnusedTypeParameter = IrritantSet.GROUP2 | ASTNode.Bit17;
+ public static final int NonnullParameterAnnotationDropped = IrritantSet.GROUP2 | ASTNode.Bit18;
//{ObjectTeams: OT/J specific problems/irritants:
public static final int OTJFlag = IrritantSet.GROUP3;
@@ -542,6 +547,9 @@ public class CompilerOptions {
/** Should null annotations of overridden methods be inherited? */
public boolean inheritNullAnnotations;
+ /** Should immediate null-check for fields be considered during null analysis (syntactical match)? */
+ public boolean enableSyntacticNullAnalysisForFields;
+
// keep in sync with warningTokenToIrritant and warningTokenFromIrritant
public final static String[] warningTokens = {
"all", //$NON-NLS-1$
@@ -819,6 +827,8 @@ public class CompilerOptions {
return OPTION_ReportNullUncheckedConversion;
case RedundantNullAnnotation :
return OPTION_ReportRedundantNullAnnotation;
+ case NonnullParameterAnnotationDropped:
+ return OPTION_ReportNonnullParameterAnnotationDropped;
}
return null;
}
@@ -1019,8 +1029,10 @@ public class CompilerOptions {
OPTION_ReportNullAnnotationInferenceConflict,
OPTION_ReportNullUncheckedConversion,
OPTION_ReportRedundantNullAnnotation,
+ OPTION_SyntacticNullAnalysisForFields,
OPTION_ReportUnusedTypeParameter,
- OPTION_InheritNullAnnotations
+ OPTION_InheritNullAnnotations,
+ OPTION_ReportNonnullParameterAnnotationDropped
};
return result;
}
@@ -1090,6 +1102,7 @@ public class CompilerOptions {
case NullUncheckedConversion :
case RedundantNullAnnotation :
case MissingNonNullByDefaultAnnotation:
+ case NonnullParameterAnnotationDropped:
return "null"; //$NON-NLS-1$
case FallthroughCase :
return "fallthrough"; //$NON-NLS-1$
@@ -1452,7 +1465,9 @@ public class CompilerOptions {
optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.')));
optionsMap.put(OPTION_ReportMissingNonNullByDefaultAnnotation, getSeverityString(MissingNonNullByDefaultAnnotation));
optionsMap.put(OPTION_ReportUnusedTypeParameter, getSeverityString(UnusedTypeParameter));
+ optionsMap.put(OPTION_SyntacticNullAnalysisForFields, this.enableSyntacticNullAnalysisForFields ? ENABLED : DISABLED);
optionsMap.put(OPTION_InheritNullAnnotations, this.inheritNullAnnotations ? ENABLED : DISABLED);
+ optionsMap.put(OPTION_ReportNonnullParameterAnnotationDropped, getSeverityString(NonnullParameterAnnotationDropped));
return optionsMap;
}
@@ -1611,6 +1626,7 @@ public class CompilerOptions {
this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME;
this.nonNullByDefaultAnnotationName = DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME;
this.intendedDefaultNonNullness = 0;
+ this.enableSyntacticNullAnalysisForFields = false;
this.inheritNullAnnotations = false;
this.analyseResourceLeaks = true;
@@ -1989,9 +2005,13 @@ public class CompilerOptions {
this.nonNullByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
}
if ((optionValue = optionsMap.get(OPTION_ReportMissingNonNullByDefaultAnnotation)) != null) updateSeverity(MissingNonNullByDefaultAnnotation, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_SyntacticNullAnalysisForFields)) != null) {
+ this.enableSyntacticNullAnalysisForFields = ENABLED.equals(optionValue);
+ }
if ((optionValue = optionsMap.get(OPTION_InheritNullAnnotations)) != null) {
this.inheritNullAnnotations = ENABLED.equals(optionValue);
}
+ if ((optionValue = optionsMap.get(OPTION_ReportNonnullParameterAnnotationDropped)) != null) updateSeverity(NonnullParameterAnnotationDropped, optionValue);
}
// Javadoc options
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
index 8efc755c8..dba49a4c4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
@@ -14,6 +14,7 @@
* bug 370639 - [compiler][resource] restore the default for resource leak warnings
* bug 265744 - Enum switch should warn about missing default
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.impl;
@@ -186,7 +187,8 @@ public class IrritantSet {
|CompilerOptions.Tasks
|CompilerOptions.UnclosedCloseable
|CompilerOptions.NullUncheckedConversion
- |CompilerOptions.RedundantNullAnnotation);
+ |CompilerOptions.RedundantNullAnnotation
+ |CompilerOptions.NonnullParameterAnnotationDropped);
// default errors IF AnnotationBasedNullAnalysis is enabled:
COMPILER_DEFAULT_ERRORS.set(
CompilerOptions.NullSpecViolation
@@ -203,7 +205,8 @@ public class IrritantSet {
.set(CompilerOptions.NullSpecViolation)
.set(CompilerOptions.NullAnnotationInferenceConflict)
.set(CompilerOptions.NullUncheckedConversion)
- .set(CompilerOptions.RedundantNullAnnotation);
+ .set(CompilerOptions.RedundantNullAnnotation)
+ .set(CompilerOptions.NonnullParameterAnnotationDropped);
RESTRICTION.set(CompilerOptions.DiscouragedReference);
STATIC_ACCESS.set(CompilerOptions.NonStaticAccessToStatic);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
index 16b38541b..22c138a29 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
@@ -8,6 +8,8 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -166,7 +168,7 @@ public int hashCode() {
/* Answer true if the receiver type can be assigned to the argument type (right)
*/
-public boolean isCompatibleWith(TypeBinding otherType) {
+public boolean isCompatibleWith(TypeBinding otherType, Scope captureScope) {
if (this == otherType)
return true;
@@ -193,7 +195,7 @@ public boolean isCompatibleWith(TypeBinding otherType) {
TypeBinding otherLowerBound;
if ((otherLowerBound = otherCapture.lowerBound) != null) {
if (!otherLowerBound.isArrayType()) return false;
- return isCompatibleWith(otherLowerBound);
+ return isCompatibleWith(otherLowerBound, captureScope);
}
}
return false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BaseTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BaseTypeBinding.java
index 40c907583..1e1178296 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BaseTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BaseTypeBinding.java
@@ -7,6 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -150,7 +152,7 @@ public final class BaseTypeBinding extends TypeBinding {
/* Answer true if the receiver type can be assigned to the argument type (right)
*/
- public final boolean isCompatibleWith(TypeBinding left) {
+ public final boolean isCompatibleWith(TypeBinding left, Scope captureScope) {
if (this == left)
return true;
int right2left = this.id + (left.id<<4);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index d823335f2..e496d9988 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -17,6 +17,7 @@
* bug 358903 - Filter practically unimportant resource leak warnings
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
* bug 388281 - [compiler][null] inheritance of null annotations as an option
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -650,6 +651,12 @@ private void createFields(IBinaryField[] iFields, long sourceLevel, char[][][] m
this.fields[i].setAnnotations(createAnnotations(binaryField.getAnnotations(), this.environment, missingTypeNames));
}
}
+ if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
+ for (int i = 0; i <size; i++) {
+ IBinaryField binaryField = iFields[i];
+ scanFieldForNullAnnotation(binaryField, this.fields[i]);
+ }
+ }
}
}
}
@@ -1576,6 +1583,42 @@ SimpleLookupTable storedAnnotations(boolean forceInitialize) {
}
return this.storedAnnotations;
}
+
+void scanFieldForNullAnnotation(IBinaryField field, FieldBinding fieldBinding) {
+ // global option is checked by caller
+ char[][] nullableAnnotationName = this.environment.getNullableAnnotationName();
+ char[][] nonNullAnnotationName = this.environment.getNonNullAnnotationName();
+ if (nullableAnnotationName == null || nonNullAnnotationName == null)
+ return; // not well-configured to use null annotations
+
+ if (fieldBinding.type == null || fieldBinding.type.isBaseType())
+ return; // null annotations are only applied to reference types
+
+ boolean explicitNullness = false;
+ IBinaryAnnotation[] annotations = field.getAnnotations();
+ if (annotations != null) {
+ for (int i = 0; i < annotations.length; i++) {
+ char[] annotationTypeName = annotations[i].getTypeName();
+ if (annotationTypeName[0] != Util.C_RESOLVED)
+ continue;
+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+ if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ fieldBinding.tagBits |= TagBits.AnnotationNonNull;
+ explicitNullness = true;
+ break;
+ }
+ if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ fieldBinding.tagBits |= TagBits.AnnotationNullable;
+ explicitNullness = true;
+ break;
+ }
+ }
+ }
+ if (!explicitNullness && (this.tagBits & TagBits.AnnotationNonNullByDefault) != 0) {
+ fieldBinding.tagBits |= TagBits.AnnotationNonNull;
+ }
+}
+
void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBinding) {
if (!this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled)
return;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index 51b3c5676..4c6d589ba 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -16,6 +16,7 @@
* Bug 349326 - [1.7] new warning for missing try-with-resources
* Bug 358903 - Filter practically unimportant resource leak warnings
* Bug 395977 - [compiler][resource] Resource leak warning behavior possibly incorrect for anonymous inner class
+ * Bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1634,7 +1635,7 @@ public class ClassScope extends Scope {
sourceType.tagBits |= (superType.tagBits & TagBits.HierarchyHasProblems); // propagate if missing supertpye
sourceType.superclass = superType;
// bound check (in case of bogus definition of Enum type)
- if (refTypeVariables[0].boundCheck(superType, sourceType) != TypeConstants.OK) {
+ if (refTypeVariables[0].boundCheck(superType, sourceType, this) != TypeConstants.OK) {
problemReporter().typeMismatchError(rootEnumType, refTypeVariables[0], sourceType, null);
}
return !foundCycle;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
index cb5b676be..428844362 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
@@ -8,9 +8,11 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * bug 185682 - Increment/decrement operators mark local variables as read
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -299,6 +301,17 @@ public Constant constant() {
return fieldConstant;
}
+public void fillInDefaultNonNullness(FieldDeclaration sourceField, Scope scope) {
+ if ( this.type != null
+ && !this.type.isBaseType()
+ && (this.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0)
+ {
+ this.tagBits |= TagBits.AnnotationNonNull;
+ } else if ((this.tagBits & TagBits.AnnotationNonNull) != 0) {
+ scope.problemReporter().nullAnnotationIsRedundant(sourceField);
+ }
+}
+
/**
* X<T> t --> LX<TT;>;
*/
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
index 81466c334..be1b76594 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -309,10 +309,8 @@ public class ImplicitNullAnnotationVerifier {
}
}
if (shouldComplain) {
- boolean needNonNull = false;
char[][] annotationName;
if (inheritedNonNullNess == Boolean.TRUE) {
- needNonNull = true;
annotationName = environment.getNonNullAnnotationName();
} else {
annotationName = environment.getNullableAnnotationName();
@@ -329,17 +327,24 @@ public class ImplicitNullAnnotationVerifier {
} else {
scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
}
- } else if (inheritedNonNullNess == Boolean.FALSE // unannotated conflics only with inherited @Nullable
- && currentNonNullNess == null)
+ } else if (currentNonNullNess == null)
{
- if (currentArgument != null) {
- scope.problemReporter().parameterLackingNullAnnotation(
+ // unannotated strictly conflicts only with inherited @Nullable
+ if (inheritedNonNullNess == Boolean.FALSE) {
+ if (currentArgument != null) {
+ scope.problemReporter().parameterLackingNullableAnnotation(
+ currentArgument,
+ inheritedMethod.declaringClass,
+ annotationName);
+ } else {
+ scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ }
+ } else if (inheritedNonNullNess == Boolean.TRUE) {
+ // not strictly a conflict, but a configurable warning is given anyway:
+ scope.problemReporter().parameterLackingNonnullAnnotation(
currentArgument,
inheritedMethod.declaringClass,
- needNonNull,
annotationName);
- } else {
- scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java
index 1d2b9dd46..e23a0b9b8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java
@@ -14,6 +14,7 @@
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -196,14 +197,6 @@ public class LocalVariableBinding extends VariableBinding {
}
}
- public boolean isNonNull() {
- return (this.tagBits & TagBits.AnnotationNonNull) != 0;
- }
-
- public boolean isNullable() {
- return (this.tagBits & TagBits.AnnotationNullable) != 0;
- }
-
// Answer whether the variable binding is a secret variable added for code gen purposes
public boolean isSecret() {
//{ObjectTeams: _OT$ variables are secret, too.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
index 7b461c313..9bc43539a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
@@ -15,6 +15,7 @@
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 388795 - [compiler] detection of name clash depends on order of super interfaces
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -781,7 +782,7 @@ MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBindi
continue next;
return inheritedMethod; // not a match
}
- } else if (inheritedTypeVariable.boundCheck(substitute, argument) != TypeConstants.OK) {
+ } else if (inheritedTypeVariable.boundCheck(substitute, argument, this.type.scope) != TypeConstants.OK) {
return inheritedMethod;
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
index 8531ba806..7dccd2cf3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -8,7 +8,9 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Technical University Berlin - extended API and implementation
- * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking
+ * Stephan Herrmann - Contributions for
+ * bug 186342 - [compiler][null] Using annotations for null checking
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -132,9 +134,9 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
if (actualReceiverType instanceof ReferenceBinding)
actualReceiverRefType = (ReferenceBinding) actualReceiverType;
}
- switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType)) {
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType, scope)) {
/* orig:
- switch (typeVariable.boundCheck(substitution, substituteForChecks)) {
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, scope)) {
:giro */
// SH}
case TypeConstants.MISMATCH :
@@ -288,7 +290,7 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
if (substitute != null) continue nextTypeParameter; // already inferred previously
TypeBinding [] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EXTENDS);
if (bounds == null) continue nextTypeParameter;
- TypeBinding[] glb = Scope.greaterLowerBound(bounds);
+ TypeBinding[] glb = Scope.greaterLowerBound(bounds, scope);
TypeBinding mostSpecificSubstitute = null;
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=341795 - Per 15.12.2.8, we should fully apply glb
if (glb != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
index 56b001244..96a37ad85 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -8,9 +8,11 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann - Contribution for bug 349326 - [1.7] new warning for missing try-with-resources
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contributions for
+ * bug 349326 - [1.7] new warning for missing try-with-resources
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -111,7 +113,7 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
TypeVariableBinding[] typeVariables = this.type.typeVariables();
if (this.arguments != null && typeVariables != null) { // arguments may be null in error cases
for (int i = 0, length = typeVariables.length; i < length; i++) {
- if (typeVariables[i].boundCheck(this, this.arguments[i]) != TypeConstants.OK) {
+ if (typeVariables[i].boundCheck(this, this.arguments[i], scope) != TypeConstants.OK) {
hasErrors = true;
if ((this.arguments[i].tagBits & TagBits.HasMissingType) == 0) {
// do not report secondary error, if type reference already got complained against
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 6404a8a3c..9b3f81188 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -16,6 +16,7 @@
* bug 358903 - Filter practically unimportant resource leak warnings
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
* bug 388281 - [compiler][null] inheritance of null annotations as an option
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1481,15 +1482,15 @@ public ReferenceBinding transferTypeArguments(ReferenceBinding other) {
* In addition to improving performance, caching also ensures there is no infinite regression
* since per nature, the compatibility check is recursive through parameterized type arguments (122775)
*/
-public boolean isCompatibleWith(TypeBinding otherType) {
+public boolean isCompatibleWith(TypeBinding otherType, /*@Nullable*/ Scope captureScope) {
//{ObjectTeams: behind the facade introduce new parameter useObjectShortcut.
- return isCompatibleWith(otherType, true);
+ return isCompatibleWith(otherType, true, captureScope);
}
// version which does not consider everything conform to Object:
-public boolean isStrictlyCompatibleWith(TypeBinding otherType) {
- return isCompatibleWith(otherType, false);
+public boolean isStrictlyCompatibleWith(TypeBinding otherType, /*@Nullable*/ Scope captureScope) {
+ return isCompatibleWith(otherType, false, captureScope);
}
-public boolean isCompatibleWith(TypeBinding otherType, boolean useObjectShortcut) {
+public boolean isCompatibleWith(TypeBinding otherType, boolean useObjectShortcut, /*@Nullable*/ Scope captureScope) {
// SH}
if (otherType == this)
return true;
@@ -1513,13 +1514,21 @@ public boolean isCompatibleWith(TypeBinding otherType, boolean useObjectShortcut
this.compatibleCache.put(otherType, Boolean.FALSE); // protect from recursive call
//{ObjectTeams: propagate:
/* orig:
- if (isCompatibleWith0(otherType)) {
+ if (isCompatibleWith0(otherType, captureScope)) {
:giro */
- if (isCompatibleWith0(otherType, useObjectShortcut)) {
+ if (isCompatibleWith0(otherType, useObjectShortcut, captureScope)) {
// SH}
this.compatibleCache.put(otherType, Boolean.TRUE);
return true;
}
+ if (captureScope == null
+ && this instanceof TypeVariableBinding
+ && ((TypeVariableBinding)this).firstBound instanceof ParameterizedTypeBinding) {
+ // see https://bugs.eclipse.org/395002#c9
+ // in this case a subsequent check with captureScope != null may actually get
+ // a better result, reset this info to ensure we're not blocking that re-check.
+ this.compatibleCache.put(otherType, null);
+ }
return false;
}
@@ -1538,7 +1547,7 @@ public void resetIncompatibleTypes() {
* Answer true if the receiver type can be assigned to the argument type (right)
*/
//{ObjectTeams: new parameter useObjectShortcut
-private boolean isCompatibleWith0(TypeBinding otherType, boolean useObjectShortcut) {
+private boolean isCompatibleWith0(TypeBinding otherType, boolean useObjectShortcut, /*@Nullable*/ Scope captureScope) {
// SH}
if (otherType == this)
return true;
@@ -1582,8 +1591,17 @@ private boolean isCompatibleWith0(TypeBinding otherType, boolean useObjectShortc
// above if same erasure
}
ReferenceBinding otherReferenceType = (ReferenceBinding) otherType;
- if (otherReferenceType.isInterface()) // could be annotation type
- return implementsInterface(otherReferenceType, true);
+ if (otherReferenceType.isInterface()) { // could be annotation type
+ if (implementsInterface(otherReferenceType, true))
+ return true;
+ if (this instanceof TypeVariableBinding && captureScope != null) {
+ TypeVariableBinding typeVariable = (TypeVariableBinding) this;
+ if (typeVariable.firstBound instanceof ParameterizedTypeBinding) {
+ TypeBinding bound = typeVariable.firstBound.capture(captureScope, -1); // no position needed as this capture will never escape this context
+ return bound.isCompatibleWith(otherReferenceType);
+ }
+ }
+ }
//{ObjectTeams: only leave if we have checked for java.lang.Object above:
/* orig:
if (isInterface()) // Explicit conversion from an interface
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index 882f065f4..d27ed0ec8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -13,6 +13,7 @@
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 387612 - Unreachable catch block...exception is never thrown from the try
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -353,7 +354,7 @@ public abstract class Scope {
}
// 5.1.10
- public static TypeBinding[] greaterLowerBound(TypeBinding[] types) {
+ public static TypeBinding[] greaterLowerBound(TypeBinding[] types, /*@Nullable*/ Scope scope) {
if (types == null) return null;
int length = types.length;
if (length == 0) return null;
@@ -366,7 +367,7 @@ public abstract class Scope {
if (i == j) continue;
TypeBinding jType = result[j];
if (jType == null) continue;
- if (iType.isCompatibleWith(jType)) { // if Vi <: Vj, Vj is removed
+ if (iType.isCompatibleWith(jType, scope)) { // if Vi <: Vj, Vj is removed
if (result == types) { // defensive copy
System.arraycopy(result, 0, result = new TypeBinding[length], 0, length);
}
@@ -488,7 +489,7 @@ public abstract class Scope {
TypeBinding [] bounds = new TypeBinding[1 + substitutedOtherBounds.length];
bounds[0] = substitutedBound;
System.arraycopy(substitutedOtherBounds, 0, bounds, 1, substitutedOtherBounds.length);
- TypeBinding[] glb = Scope.greaterLowerBound(bounds); // re-evaluate
+ TypeBinding[] glb = Scope.greaterLowerBound(bounds, null); // re-evaluate
if (glb != null && glb != bounds) {
substitutedBound = glb[0];
if (glb.length == 1) {
@@ -3748,7 +3749,7 @@ public abstract class Scope {
case Wildcard.SUPER :
// ? super U, ? super V
if (wildU.boundKind == Wildcard.SUPER) {
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound});
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound}, this);
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
}
@@ -3764,7 +3765,7 @@ public abstract class Scope {
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
// U, ? super V
case Wildcard.SUPER :
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound});
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound}, this);
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :
@@ -3782,7 +3783,7 @@ public abstract class Scope {
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
// U, ? super V
case Wildcard.SUPER :
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v});
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v}, this);
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :
@@ -4581,7 +4582,7 @@ public abstract class Scope {
private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, LookupEnvironment env, boolean tieBreakingVarargsMethods) {
// only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5
- if (arg.isCompatibleWith(param))
+ if (arg.isCompatibleWith(param, this))
return COMPATIBLE;
if (tieBreakingVarargsMethods && (this.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_7 || !CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation)) {
/* 15.12.2.5 Choosing the Most Specific Method, ... One variable arity member method named m is more specific than
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 6994dabda..6673003fb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -21,6 +21,8 @@
* bug 384663 - Package Based Annotation Compilation Error in JDT 3.8/4.2 (works in 3.7.2)
* bug 386356 - Type mismatch error with annotations and generics
* bug 388281 - [compiler][null] inheritance of null annotations as an option
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 380896 - [compiler][null] Enum constants not recognised as being NonNull.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1990,75 +1992,91 @@ public FieldBinding resolveTypeFor(FieldBinding field) {
if (fieldDecls[f].binding != field)
continue;
- MethodScope initializationScope = field.isStatic()
- ? this.scope.referenceContext.staticInitializerScope
- : this.scope.referenceContext.initializerScope;
- FieldBinding previousField = initializationScope.initializedField;
- try {
- initializationScope.initializedField = field;
- FieldDeclaration fieldDecl = fieldDecls[f];
- TypeBinding fieldType =
- fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT
- ? initializationScope.environment().convertToRawType(this, false /*do not force conversion of enclosing types*/) // enum constant is implicitly of declaring enum type
- : fieldDecl.type.resolveType(initializationScope, true /* check bounds*/);
- field.type = fieldType;
- field.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
- if (fieldType == null) {
- fieldDecl.binding = null;
- return null;
- }
- if (fieldType == TypeBinding.VOID) {
- this.scope.problemReporter().variableTypeCannotBeVoid(fieldDecl);
- fieldDecl.binding = null;
- return null;
- }
- if (fieldType.isArrayType() && ((ArrayBinding) fieldType).leafComponentType == TypeBinding.VOID) {
- this.scope.problemReporter().variableTypeCannotBeVoidArray(fieldDecl);
- fieldDecl.binding = null;
- return null;
- }
- if ((fieldType.tagBits & TagBits.HasMissingType) != 0) {
- field.tagBits |= TagBits.HasMissingType;
- }
- TypeBinding leafType = fieldType.leafComponentType();
- if (leafType instanceof ReferenceBinding && (((ReferenceBinding)leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) {
- field.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
+ MethodScope initializationScope = field.isStatic()
+ ? this.scope.referenceContext.staticInitializerScope
+ : this.scope.referenceContext.initializerScope;
+ FieldBinding previousField = initializationScope.initializedField;
+ try {
+ initializationScope.initializedField = field;
+ FieldDeclaration fieldDecl = fieldDecls[f];
+ TypeBinding fieldType =
+ fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT
+ ? initializationScope.environment().convertToRawType(this, false /*do not force conversion of enclosing types*/) // enum constant is implicitly of declaring enum type
+ : fieldDecl.type.resolveType(initializationScope, true /* check bounds*/);
+ field.type = fieldType;
+ field.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
+ if (fieldType == null) {
+ fieldDecl.binding = null;
+ return null;
+ }
+ if (fieldType == TypeBinding.VOID) {
+ this.scope.problemReporter().variableTypeCannotBeVoid(fieldDecl);
+ fieldDecl.binding = null;
+ return null;
+ }
+ if (fieldType.isArrayType() && ((ArrayBinding) fieldType).leafComponentType == TypeBinding.VOID) {
+ this.scope.problemReporter().variableTypeCannotBeVoidArray(fieldDecl);
+ fieldDecl.binding = null;
+ return null;
+ }
+ if ((fieldType.tagBits & TagBits.HasMissingType) != 0) {
+ field.tagBits |= TagBits.HasMissingType;
+ }
+ TypeBinding leafType = fieldType.leafComponentType();
+ if (leafType instanceof ReferenceBinding && (((ReferenceBinding)leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) {
+ field.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
+ }
+
+ // apply null default:
+ LookupEnvironment environment = this.scope.environment();
+ if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
+ if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
+ // enum constants neither have a type declaration nor can they be null
+ field.tagBits |= TagBits.AnnotationNonNull;
+ } else {
+ initializeNullDefault();
+ if (hasNonNullDefault()) {
+ field.fillInDefaultNonNullness(fieldDecl, initializationScope);
+ }
+ // validate null annotation:
+ this.scope.validateNullAnnotation(field.tagBits, fieldDecl.type, fieldDecl.annotations);
}
- } finally {
- initializationScope.initializedField = previousField;
}
+ } finally {
+ initializationScope.initializedField = previousField;
+ }
//{ObjectTeams: copy-inherited fields and anchored types:
- if (fieldDecls[f].getKind() != AbstractVariableDeclaration.ENUM_CONSTANT) {
- if (fieldDecls[f].type == null) // should not happen for non-enum types
- throw new InternalCompilerError("Field "+fieldDecls[f]+" has no type in "+this);
+ if (fieldDecls[f].getKind() != AbstractVariableDeclaration.ENUM_CONSTANT) {
+ if (fieldDecls[f].type == null) // should not happen for non-enum types
+ throw new InternalCompilerError("Field "+fieldDecls[f]+" has no type in "+this);
- field.copyInheritanceSrc = fieldDecls[f].copyInheritanceSrc;
- field.maybeSetFieldTypeAnchorAttribute();
- // anchored to tthis?
- field.type = RoleTypeCreator.maybeWrapUnqualifiedRoleType(this.scope, field.type, fieldDecls[f].type);
- if (field.couldBeTeamAnchor()) {
- // link decl and binding via model
- // for early resolving from TeamAnchor.hasSameBestNameAs()
- FieldModel.getModel(fieldDecls[f]).setBinding(field);
- }
+ field.copyInheritanceSrc = fieldDecls[f].copyInheritanceSrc;
+ field.maybeSetFieldTypeAnchorAttribute();
+ // anchored to tthis?
+ field.type = RoleTypeCreator.maybeWrapUnqualifiedRoleType(this.scope, field.type, fieldDecls[f].type);
+ if (field.couldBeTeamAnchor()) {
+ // link decl and binding via model
+ // for early resolving from TeamAnchor.hasSameBestNameAs()
+ FieldModel.getModel(fieldDecls[f]).setBinding(field);
}
- // need role field bridges?
- if ( isRole()
- && ((field.modifiers & ClassFileConstants.AccPrivate) != 0)
- && !CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, field.name))
- {
- MethodBinding inner;
- ReferenceBinding originalRole = field.declaringClass;
- if (field.copyInheritanceSrc != null)
- originalRole = field.copyInheritanceSrc.declaringClass;
- inner = FieldModel.getDecapsulatingFieldAccessor(this, field, true/*isGetter*/);
+ }
+ // need role field bridges?
+ if ( isRole()
+ && ((field.modifiers & ClassFileConstants.AccPrivate) != 0)
+ && !CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, field.name))
+ {
+ MethodBinding inner;
+ ReferenceBinding originalRole = field.declaringClass;
+ if (field.copyInheritanceSrc != null)
+ originalRole = field.copyInheritanceSrc.declaringClass;
+ inner = FieldModel.getDecapsulatingFieldAccessor(this, field, true/*isGetter*/);
+ ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
+ if (!field.isFinal()) { // no setter for final (includes all static role fields)
+ // otherwise we would have to handle different signatures (w/ w/o role arg), which we currently don't
+ inner = FieldModel.getDecapsulatingFieldAccessor(this, field, false/*isGetter*/);
((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
- if (!field.isFinal()) { // no setter for final (includes all static role fields)
- // otherwise we would have to handle different signatures (w/ w/o role arg), which we currently don't
- inner = FieldModel.getDecapsulatingFieldAccessor(this, field, false/*isGetter*/);
- ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
- }
}
+ }
// SH}
return field;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
index b45fb89f1..ac089680e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
@@ -8,9 +8,11 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephen Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 317046
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephen Herrmann <stephan@cs.tu-berlin.de> - Contributions for
+ * bug 317046 - Exception during debugging when hover mouse over a field
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -476,7 +478,11 @@ public boolean isClass() {
/* Answer true if the receiver type can be assigned to the argument type (right)
*/
-public abstract boolean isCompatibleWith(TypeBinding right);
+public boolean isCompatibleWith(TypeBinding right) {
+ return isCompatibleWith(right, null); // delegate from the old signature to the new implementation:
+}
+// version that allows to capture a type bound using 'scope':
+public abstract boolean isCompatibleWith(TypeBinding right, /*@Nullable*/ Scope scope);
public boolean isEnum() {
return false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
index c5cd45102..71c27915a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -13,6 +13,7 @@
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 359362 - FUP of bug 349326: Resource leak on non-Closeable resource
* bug 358903 - Filter practically unimportant resource leak warnings
+ * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -64,12 +65,26 @@ public class TypeVariableBinding extends ReferenceBinding {
* Returns true if the argument type satisfies all bounds of the type parameter
*/
//{ObjectTeams: added optional argument actualReceiverType:
- public int boundCheck(Substitution substitution, TypeBinding argumentType) {
- return boundCheck(substitution, argumentType, null);
+ public int boundCheck(Substitution substitution, TypeBinding argumentType, Scope scope) {
+ return boundCheck(substitution, argumentType, null, scope);
}
- public int boundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType) {
+ public int boundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope) {
+ int code = internalBoundCheck(substitution, argumentType, actualReceiverType, scope);
+// SH}
+ if (code == TypeConstants.MISMATCH) {
+ if (argumentType instanceof TypeVariableBinding && scope != null) {
+ TypeBinding bound = ((TypeVariableBinding)argumentType).firstBound;
+ if (bound instanceof ParameterizedTypeBinding) {
+ int code2 = boundCheck(substitution, bound.capture(scope, -1), scope); // no position needed as this capture will never escape this context
+ return Math.min(code, code2);
+ }
+ }
+ }
+ return code;
+ }
+//{ObjectTeams: added optional argument actualReceiverType:
+ private int internalBoundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope) {
// SH}
-
if (argumentType == TypeBinding.NULL || argumentType == this) {
return TypeConstants.OK;
}
@@ -93,7 +108,7 @@ public class TypeVariableBinding extends ReferenceBinding {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass;
if (substitutedSuperType.id != TypeIds.T_JavaLangObject) {
if (isArrayBound) {
- if (!wildcardBound.isCompatibleWith(substitutedSuperType))
+ if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope))
return TypeConstants.MISMATCH;
} else {
TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType);
@@ -120,7 +135,7 @@ public class TypeVariableBinding extends ReferenceBinding {
for (int i = 0, length = this.superInterfaces.length; i < length; i++) {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i];
if (isArrayBound) {
- if (!wildcardBound.isCompatibleWith(substitutedSuperType))
+ if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope))
return TypeConstants.MISMATCH;
} else {
TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType);
@@ -140,7 +155,7 @@ public class TypeVariableBinding extends ReferenceBinding {
// if the wildcard is lower-bounded by a type variable that has no relevant upper bound there's nothing to check here (bug 282152):
if (wildcard.bound.isTypeVariable() && ((TypeVariableBinding)wildcard.bound).superclass.id == TypeIds.T_JavaLangObject)
break;
- return boundCheck(substitution, wildcard.bound);
+ return boundCheck(substitution, wildcard.bound, scope);
case Wildcard.UNBOUND :
break;
@@ -151,7 +166,7 @@ public class TypeVariableBinding extends ReferenceBinding {
if (this.superclass.id != TypeIds.T_JavaLangObject) {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass;
if (substitutedSuperType != argumentType) {
- if (!argumentType.isCompatibleWith(substitutedSuperType)) {
+ if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) {
return TypeConstants.MISMATCH;
}
TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType);
@@ -206,7 +221,7 @@ public class TypeVariableBinding extends ReferenceBinding {
for (int i = 0, length = this.superInterfaces.length; i < length; i++) {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i];
if (substitutedSuperType != argumentType) {
- if (!argumentType.isCompatibleWith(substitutedSuperType)) {
+ if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) {
return TypeConstants.MISMATCH;
}
TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedAnnotationBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedAnnotationBinding.java
index fe6d35db7..57952eaa7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedAnnotationBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedAnnotationBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -21,8 +21,14 @@ UnresolvedAnnotationBinding(ReferenceBinding type, ElementValuePair[] pairs, Loo
public ReferenceBinding getAnnotationType() {
if (this.typeUnresolved) { // the type is resolved when requested
- this.type = (ReferenceBinding) BinaryTypeBinding.resolveType(this.type, this.env, false /* no raw conversion for now */);
- // annotation type are never parameterized
+ boolean wasToleratingMissingTypeProcessingAnnotations = this.env.mayTolerateMissingType;
+ this.env.mayTolerateMissingType = true; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=388042
+ try {
+ this.type = (ReferenceBinding) BinaryTypeBinding.resolveType(this.type, this.env, false /* no raw conversion for now */);
+ // annotation types are never parameterized
+ } finally {
+ this.env.mayTolerateMissingType = wasToleratingMissingTypeProcessingAnnotations;
+ }
this.typeUnresolved = false;
}
return this.type;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
index 002af3139..217afd82f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
@@ -9,6 +9,8 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 331649 - [compiler][null] consider null annotations for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -75,7 +77,17 @@ public abstract class VariableBinding
public final boolean isEffectivelyFinal() {
return (this.tagBits & TagBits.IsEffectivelyFinal) != 0;
}
-
+
+ /** Answer true if null annotations are enabled and this field is specified @NonNull */
+ public boolean isNonNull() {
+ return (this.tagBits & TagBits.AnnotationNonNull) != 0;
+ }
+
+ /** Answer true if null annotations are enabled and this field is specified @Nullable */
+ public boolean isNullable() {
+ return (this.tagBits & TagBits.AnnotationNullable) != 0;
+ }
+
public char[] readableName() {
//{ObjectTeams: pretty printing for generated names:
if (CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, this.name))
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 8bcd33809..0ab0f2d51 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -13,6 +13,7 @@
* Stephan Herrmann - Contributions for
* bug 366003 - CCE in ASTNode.resolveAnnotations(ASTNode.java:639)
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 393719 - [compiler] inconsistent warnings on iteration variables
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
@@ -3951,6 +3952,10 @@ protected void consumeEnhancedForStatementHeader(){
this.expressionLengthPtr--;
final Expression collection = this.expressionStack[this.expressionPtr--];
statement.collection = collection;
+ // https://bugs.eclipse.org/393719 - [compiler] inconsistent warnings on iteration variables
+ // let declaration(Source)End include the collection to achieve that @SuppressWarnings affects this part, too:
+ statement.elementVariable.declarationSourceEnd = collection.sourceEnd;
+ statement.elementVariable.declarationEnd = collection.sourceEnd;
statement.sourceEnd = this.rParenPos;
if(!this.statementRecoveryActivated &&
@@ -3967,6 +3972,7 @@ protected void consumeEnhancedForStatementHeaderInit(boolean hasModifiers) {
LocalDeclaration localDeclaration = createLocalDeclaration(identifierName, (int) (namePosition >>> 32), (int) namePosition);
localDeclaration.declarationSourceEnd = localDeclaration.declarationEnd;
+ localDeclaration.bits |= ASTNode.IsForeachElementVariable;
int extraDims = this.intStack[this.intPtr--];
this.identifierPtr--;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index 5765e0bcf..9afa1200f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -22,6 +22,10 @@
* bug 374605 - Unreasonable warning for enum-based switch statements
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 376053 - [compiler][resource] Strange potential resource leak problems
+ * bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
+ * bug 393719 - [compiler] inconsistent warnings on iteration variables
+ * bug 331649 - [compiler][null] consider null annotations for fields
+ * bug 382789 - [compiler][null] warn when syntactically-nonnull expression is compared against null
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.problem;
@@ -50,6 +54,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
@@ -81,6 +86,7 @@ import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
@@ -318,6 +324,7 @@ public static int getIrritant(int problemID) {
case IProblem.UnsafeRawConstructorInvocation:
case IProblem.UnsafeRawMethodInvocation:
case IProblem.UnsafeTypeConversion:
+ case IProblem.UnsafeElementTypeConversion:
case IProblem.UnsafeRawFieldAssignment:
case IProblem.UnsafeGenericCast:
case IProblem.UnsafeReturnTypeOverride:
@@ -356,6 +363,7 @@ public static int getIrritant(int problemID) {
return CompilerOptions.VarargsArgumentNeedCast;
case IProblem.NullLocalVariableReference:
+ case IProblem.NullableFieldReference:
return CompilerOptions.NullReference;
case IProblem.PotentialNullLocalVariableReference:
@@ -368,9 +376,14 @@ public static int getIrritant(int problemID) {
case IProblem.NonNullLocalVariableComparisonYieldsFalse:
case IProblem.NullLocalVariableComparisonYieldsFalse:
case IProblem.NullLocalVariableInstanceofYieldsFalse:
+ case IProblem.RedundantNullCheckOnNonNullExpression:
+ case IProblem.NonNullExpressionComparisonYieldsFalse:
case IProblem.RedundantNullCheckOnNonNullMessageSend:
case IProblem.RedundantNullCheckOnSpecdNonNullLocalVariable:
case IProblem.SpecdNonNullLocalVariableComparisonYieldsFalse:
+ case IProblem.NonNullMessageSendComparisonYieldsFalse:
+ case IProblem.RedundantNullCheckOnNonNullSpecdField:
+ case IProblem.NonNullSpecdFieldComparisonYieldsFalse:
return CompilerOptions.RedundantNullCheck;
case IProblem.RequiredNonNullButProvidedNull:
@@ -378,13 +391,17 @@ public static int getIrritant(int problemID) {
case IProblem.IllegalReturnNullityRedefinition:
case IProblem.IllegalRedefinitionToNonNullParameter:
case IProblem.IllegalDefinitionToNonNullParameter:
- case IProblem.ParameterLackingNonNullAnnotation:
case IProblem.ParameterLackingNullableAnnotation:
case IProblem.CannotImplementIncompatibleNullness:
+ case IProblem.UninitializedNonNullField:
+ case IProblem.UninitializedNonNullFieldHintMissingDefault:
case IProblem.ConflictingNullAnnotations:
case IProblem.ConflictingInheritedNullAnnotations:
return CompilerOptions.NullSpecViolation;
+ case IProblem.ParameterLackingNonNullAnnotation:
+ return CompilerOptions.NonnullParameterAnnotationDropped;
+
case IProblem.RequiredNonNullButProvidedPotentialNull:
return CompilerOptions.NullAnnotationInferenceConflict;
case IProblem.RequiredNonNullButProvidedUnknown:
@@ -787,6 +804,7 @@ public static int getProblemCategory(int severity, int problemID) {
case CompilerOptions.NullAnnotationInferenceConflict :
case CompilerOptions.NullUncheckedConversion :
case CompilerOptions.MissingNonNullByDefaultAnnotation:
+ case CompilerOptions.NonnullParameterAnnotationDropped:
return CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM;
case CompilerOptions.RedundantNullAnnotation :
return CategorizedProblem.CAT_UNNECESSARY_CODE;
@@ -5744,6 +5762,92 @@ public void localVariableNullComparedToNonNull(LocalVariableBinding local, ASTNo
nodeSourceEnd(local, location));
}
+/**
+ * @param expr expression being compared for null or nonnull
+ * @param checkForNull true if checking for null, false if checking for nonnull
+ */
+public boolean expressionNonNullComparison(Expression expr, boolean checkForNull) {
+ int problemId = 0;
+ Binding binding = null;
+ String[] arguments = null;
+ int start = 0, end = 0;
+
+ Expression location = expr;
+ // unwrap uninteresting nodes:
+ while (true) {
+ if (expr instanceof Assignment)
+ return false; // don't report against the assignment, but the variable
+ else if (expr instanceof CastExpression)
+ expr = ((CastExpression) expr).expression;
+ else
+ break;
+ }
+ // check all those kinds of expressions that can possible answer NON_NULL from nullStatus():
+ if (expr instanceof MessageSend) {
+ problemId = checkForNull
+ ? IProblem.NonNullMessageSendComparisonYieldsFalse
+ : IProblem.RedundantNullCheckOnNonNullMessageSend;
+ MethodBinding method = ((MessageSend)expr).binding;
+ binding = method;
+ arguments = new String[] { new String(method.shortReadableName()) };
+ start = location.sourceStart;
+ end = location.sourceEnd;
+ } else if (expr instanceof Reference && !(expr instanceof ThisReference) && !(expr instanceof ArrayReference)) {
+ FieldBinding field = ((Reference)expr).lastFieldBinding();
+ if (field == null) {
+ return false;
+ }
+ if (field.isNonNull()) {
+ problemId = checkForNull
+ ? IProblem.NonNullSpecdFieldComparisonYieldsFalse
+ : IProblem.RedundantNullCheckOnNonNullSpecdField;
+ char[][] nonNullName = this.options.nonNullAnnotationName;
+ arguments = new String[] { new String(field.name),
+ new String(nonNullName[nonNullName.length-1]) };
+ }
+ binding = field;
+ start = nodeSourceStart(binding, location);
+ end = nodeSourceEnd(binding, location);
+ } else if (expr instanceof AllocationExpression
+ || expr instanceof ArrayAllocationExpression
+ || expr instanceof ArrayInitializer
+ || expr instanceof ClassLiteralAccess
+ || expr instanceof ThisReference) {
+ // fall through to bottom
+ } else if (expr instanceof Literal
+ || expr instanceof ConditionalExpression) {
+ if (expr instanceof NullLiteral) {
+ needImplementation(location); // reported as nonnull??
+ return false;
+ }
+ if (expr.resolvedType != null && expr.resolvedType.isBaseType()) {
+ // false alarm, auto(un)boxing is involved
+ return false;
+ }
+ // fall through to bottom
+ } else if (expr instanceof BinaryExpression) {
+ if ((expr.bits & ASTNode.ReturnTypeIDMASK) != TypeIds.T_JavaLangString) {
+ // false alarm, primitive types involved, must be auto(un)boxing?
+ return false;
+ }
+ // fall through to bottom
+ } else {
+ needImplementation(expr); // want to see if we get here
+ return false;
+ }
+ if (problemId == 0) {
+ // standard case, fill in details now
+ problemId = checkForNull
+ ? IProblem.NonNullExpressionComparisonYieldsFalse
+ : IProblem.RedundantNullCheckOnNonNullExpression;
+ start = location.sourceStart;
+ end = location.sourceEnd;
+ arguments = NoArgument;
+ }
+ this.handle(problemId, arguments, arguments, start, end);
+ return true;
+}
+
public void localVariableNullInstanceof(LocalVariableBinding local, ASTNode location) {
int severity = computeSeverity(IProblem.NullLocalVariableInstanceofYieldsFalse);
if (severity == ProblemSeverities.Ignore) return;
@@ -5799,6 +5903,18 @@ public void localVariablePotentialNullReference(LocalVariableBinding local, ASTN
nodeSourceEnd(local, location));
}
+public void nullableFieldDereference(VariableBinding variable, long position) {
+ String[] arguments = new String[] {new String(variable.name)};
+ char[][] nullableName = this.options.nullableAnnotationName;
+ arguments = new String[] {new String(variable.name), new String(nullableName[nullableName.length-1])};
+ this.handle(
+ IProblem.NullableFieldReference,
+ arguments,
+ arguments,
+ (int)(position >>> 32),
+ (int)(position));
+}
+
public void localVariableRedundantCheckOnNonNull(LocalVariableBinding local, ASTNode location) {
int severity = computeSeverity(IProblem.RedundantNullCheckOnNonNullLocalVariable);
if (severity == ProblemSeverities.Ignore) return;
@@ -7947,6 +8063,19 @@ public void uninitializedBlankFinalField(FieldBinding field, ASTNode location) {
nodeSourceStart(field, location),
nodeSourceEnd(field, location));
}
+public void uninitializedNonNullField(FieldBinding field, ASTNode location) {
+ char[][] nonNullAnnotationName = this.options.nonNullAnnotationName;
+ String[] arguments = new String[] {
+ new String(nonNullAnnotationName[nonNullAnnotationName.length-1]),
+ new String(field.readableName())
+ };
+ this.handle(
+ methodHasMissingSwitchDefault() ? IProblem.UninitializedNonNullFieldHintMissingDefault : IProblem.UninitializedNonNullField,
+ arguments,
+ arguments,
+ nodeSourceStart(field, location),
+ nodeSourceEnd(field, location));
+}
public void uninitializedLocalVariable(LocalVariableBinding binding, ASTNode location) {
binding.tagBits |= TagBits.NotInitialized;
String[] arguments = new String[] {new String(binding.readableName())};
@@ -8334,6 +8463,21 @@ public void unsafeTypeConversion(Expression expression, TypeBinding expressionTy
expression.sourceStart,
expression.sourceEnd);
}
+public void unsafeElementTypeConversion(Expression expression, TypeBinding expressionType, TypeBinding expectedType) {
+ if (this.options.sourceLevel < ClassFileConstants.JDK1_5) return; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=305259
+ int severity = computeSeverity(IProblem.UnsafeElementTypeConversion);
+ if (severity == ProblemSeverities.Ignore) return;
+ if (!this.options.reportUnavoidableGenericTypeProblems && expression.forcedToBeRaw(this.referenceContext)) {
+ return;
+ }
+ this.handle(
+ IProblem.UnsafeElementTypeConversion,
+ new String[] { new String(expressionType.readableName()), new String(expectedType.readableName()), new String(expectedType.erasure().readableName()) },
+ new String[] { new String(expressionType.shortReadableName()), new String(expectedType.shortReadableName()), new String(expectedType.erasure().shortReadableName()) },
+ severity,
+ expression.sourceStart,
+ expression.sourceEnd);
+}
public void unusedArgument(LocalDeclaration localDecl) {
int severity = computeSeverity(IProblem.ArgumentIsNeverUsed);
if (severity == ProblemSeverities.Ignore) return;
@@ -8970,14 +9114,13 @@ public void nullityMismatch(Expression expression, TypeBinding providedType, Typ
return;
}
if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) {
- if (expression instanceof SingleNameReference) {
- SingleNameReference snr = (SingleNameReference) expression;
- if (snr.binding instanceof LocalVariableBinding) {
- if (((LocalVariableBinding)snr.binding).isNullable()) {
- nullityMismatchSpecdNullable(expression, requiredType, annotationName);
- return;
- }
- }
+ VariableBinding var = expression.localVariableBinding();
+ if (var == null && expression instanceof Reference) {
+ var = ((Reference)expression).lastFieldBinding();
+ }
+ if (var != null && var.isNullable()) {
+ nullityMismatchSpecdNullable(expression, requiredType, annotationName);
+ return;
}
nullityMismatchPotentiallyNull(expression, requiredType, annotationName);
return;
@@ -12666,14 +12809,30 @@ public void illegalRedefinitionToNonNullParameter(Argument argument, ReferenceBi
argument.type.sourceEnd);
}
}
-public void parameterLackingNullAnnotation(Argument argument, ReferenceBinding declaringClass, boolean needNonNull, char[][] inheritedAnnotationName) {
+public void parameterLackingNullableAnnotation(Argument argument, ReferenceBinding declaringClass, char[][] inheritedAnnotationName) {
this.handle(
- needNonNull ? IProblem.ParameterLackingNonNullAnnotation : IProblem.ParameterLackingNullableAnnotation,
+ IProblem.ParameterLackingNullableAnnotation,
new String[] { new String(argument.name), new String(declaringClass.readableName()), CharOperation.toString(inheritedAnnotationName)},
new String[] { new String(argument.name), new String(declaringClass.shortReadableName()), new String(inheritedAnnotationName[inheritedAnnotationName.length-1])},
argument.type.sourceStart,
argument.type.sourceEnd);
}
+public void parameterLackingNonnullAnnotation(Argument argument, ReferenceBinding declaringClass, char[][] inheritedAnnotationName) {
+ int sourceStart = 0, sourceEnd = 0;
+ if (argument != null) {
+ sourceStart = argument.type.sourceStart;
+ sourceEnd = argument.type.sourceEnd;
+ } else if (this.referenceContext instanceof TypeDeclaration) {
+ sourceStart = ((TypeDeclaration) this.referenceContext).sourceStart;
+ sourceEnd = ((TypeDeclaration) this.referenceContext).sourceEnd;
+ }
+ this.handle(
+ IProblem.ParameterLackingNonNullAnnotation,
+ new String[] { new String(declaringClass.readableName()), CharOperation.toString(inheritedAnnotationName)},
+ new String[] { new String(declaringClass.shortReadableName()), new String(inheritedAnnotationName[inheritedAnnotationName.length-1])},
+ sourceStart,
+ sourceEnd);
+}
public void illegalReturnRedefinition(AbstractMethodDeclaration abstractMethodDecl, MethodBinding inheritedMethod, char[][] nonNullAnnotationName) {
MethodDeclaration methodDecl = (MethodDeclaration) abstractMethodDecl;
StringBuffer methodSignature = new StringBuffer();
@@ -12758,6 +12917,13 @@ public void nullAnnotationIsRedundant(AbstractMethodDeclaration sourceMethod, in
this.handle(IProblem.RedundantNullAnnotation, ProblemHandler.NoArgument, ProblemHandler.NoArgument, sourceStart, sourceEnd);
}
+public void nullAnnotationIsRedundant(FieldDeclaration sourceField) {
+ Annotation annotation = findAnnotation(sourceField.annotations, TypeIds.T_ConfiguredAnnotationNonNull);
+ int sourceStart = annotation != null ? annotation.sourceStart : sourceField.type.sourceStart;
+ int sourceEnd = sourceField.type.sourceEnd;
+ this.handle(IProblem.RedundantNullAnnotation, ProblemHandler.NoArgument, ProblemHandler.NoArgument, sourceStart, sourceEnd);
+}
+
public void nullDefaultAnnotationIsRedundant(ASTNode location, Annotation[] annotations, Binding outer) {
Annotation annotation = findAnnotation(annotations, TypeIds.T_ConfiguredAnnotationNonNullByDefault);
int start = annotation != null ? annotation.sourceStart : location.sourceStart;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index 60f8ffaff..33aa94105 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -19,6 +19,10 @@
# bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
# bug 374605 - Unreasonable warning for enum-based switch statements
# bug 388281 - [compiler][null] inheritance of null annotations as an option
+# bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
+# bug 393719 - [compiler] inconsistent warnings on iteration variables
+# bug 331649 - [compiler][null] consider null annotations for fields
+# bug 382789 - [compiler][null] warn when syntactically-nonnull expression is compared against null
###############################################################################
0 = {0}
1 = super cannot be used in java.lang.Object
@@ -533,6 +537,7 @@
580 = Type mismatch: cannot convert from element type {0} to {1}
581 = Can only iterate over an array or an instance of java.lang.Iterable
582 = Can only iterate over an array or an instance of java.util.Collection
+585 = Type safety: Elements of type {0} need unchecked conversion to conform to {1}
### SOURCE LEVEL
590 = Syntax error, type parameters are only available if source level is 1.5 or greater
@@ -592,6 +597,10 @@
### MORE TYPE RELATED
662 = Illegal attempt to create arrays of union types
+### NULL ANALYSIS FOR OTHER EXPRESSIONS
+670 = Null comparison always yields false: this expression cannot be null
+671 = Redundant null check: this expression cannot be null
+
### CORRUPTED BINARIES
700 = The class file {0} contains a signature ''{1}'' ill-formed at position {2}
@@ -681,13 +690,14 @@
914 = The return type is incompatible with the @{1} return from {0}
915 = Illegal redefinition of parameter {0}, inherited method from {1} declares this parameter as @{2}
916 = Illegal redefinition of parameter {0}, inherited method from {1} does not constrain this parameter
-917 = Missing non-null annotation: inherited method from {1} declares this parameter as @{2}
+917 = Missing non-null annotation: inherited method from {0} declares this parameter as @{1}
918 = Missing nullable annotation: inherited method from {1} declares this parameter as @{2}
919 = Potential null pointer access: The method {0} may return null
920 = Redundant null check: The method {0} cannot return null
921 = The method {0} from {1} cannot implement the corresponding method from {2} due to incompatible nullness constraints
922 = The nullness annotation is redundant with a default that applies to this location
923 = The nullness annotation @{0} is not applicable for the primitive type {1}
+924 = Potential null pointer access: The field {0} is declared as @{1}
925 = Nullness default is redundant with the global default
926 = Nullness default is redundant with a default specified for the enclosing package {0}
927 = Nullness default is redundant with a default specified for the enclosing type {0}
@@ -697,6 +707,11 @@
931 = Redundant null check: The variable {0} is specified as @{1}
932 = Null comparison always yields false: The variable {0} is specified as @{1}
933 = Null type mismatch: required ''@{0} {1}'' but the provided value is specified as @{2}
+934 = The @{0} field {1} may not have been initialized
+935 = The @{0} field {1} may not have been initialized. Note that a problem regarding missing ''default:'' on ''switch'' has been suppressed, which is perhaps related to this problem
+936 = Null comparison always yields false: The method {0} cannot return null
+937 = Redundant null check: The field {0} is declared as @{1}
+938 = Null comparison always yields false: The field {0} is declared as @{1}
939 = The default ''@{0}'' conflicts with the inherited ''@{1}'' annotation in the overridden method from {2}
940 = Conflict between inherited null annotations ''@{0}'' declared in {1} versus ''@{2}'' declared in {3}

Back to the top