### Eclipse Workspace Patch 1.0
#P org.eclipse.jdt.core
Index: batch/org/eclipse/jdt/internal/compiler/batch/Main.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java,v
retrieving revision 1.358
diff -u -r1.358 Main.java
--- batch/org/eclipse/jdt/internal/compiler/batch/Main.java 17 Dec 2010 09:38:57 -0000 1.358
+++ batch/org/eclipse/jdt/internal/compiler/batch/Main.java 19 Dec 2010 20:25:00 -0000
@@ -12,6 +12,7 @@
* Benjamin Muskalla - Contribution for bug 239066
* Stephan Herrmann - Contribution for bug 236385
* Stephan Herrmann - Contribution for bug 295551
+ * Stephan Herrmann - Contribution for bug 186342
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.batch;
@@ -2388,6 +2389,27 @@
mode = INSIDE_WARNINGS_PROPERTIES;
continue;
}
+ if (currentArg.startsWith("-nullAnnotations:")) { //$NON-NLS-1$
+ StringTokenizer tokenizer =
+ new StringTokenizer(currentArg.substring("-nullAnnotations:".length()), ","); //$NON-NLS-1$ //$NON-NLS-2$
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if (token.startsWith("nullable=")) { //$NON-NLS-1$
+ this.options.put(CompilerOptions.OPTION_NullableAnnotationName, token.substring("nullable=".length())); //$NON-NLS-1$
+ } else if (token.startsWith("nonnull=")) { //$NON-NLS-1$
+ this.options.put(CompilerOptions.OPTION_NonNullAnnotationName, token.substring("nonnull=".length())); //$NON-NLS-1$
+ } else if (token.equals("emulate")) { //$NON-NLS-1$
+ this.options.put(CompilerOptions.OPTION_EmulateNullAnnotationTypes, CompilerOptions.ENABLED);
+ } else if (token.equals("import")) { //$NON-NLS-1$
+ this.options.put(CompilerOptions.OPTION_DefaultImportNullAnnotationTypes, CompilerOptions.ENABLED);
+ } else {
+ throw new IllegalArgumentException(
+ this.bind("configure.unrecognized.nullannotation.option", token)); //$NON-NLS-1$
+ }
+ }
+ mode = DEFAULT;
+ continue;
+ }
break;
case INSIDE_TARGET :
if (this.didSpecifyTarget) {
Index: batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties,v
retrieving revision 1.947
diff -u -r1.947 messages.properties
--- batch/org/eclipse/jdt/internal/compiler/batch/messages.properties 17 Dec 2010 09:38:55 -0000 1.947
+++ batch/org/eclipse/jdt/internal/compiler/batch/messages.properties 19 Dec 2010 20:25:01 -0000
@@ -8,6 +8,7 @@
# Contributors:
# IBM Corporation - initial API and implementation
# Benjamin Muskalla - Contribution for bug 239066
+# Stephan Herrmann - Contribution for bug 186342
###############################################################################
### JavaBatchCompiler messages.
@@ -71,6 +72,7 @@
## configure.directoryNotExist = directory does not exist: {0}
configure.unrecognizedOption = Unrecognized option : {0}
+configure.unrecognized.nullannotation.option = Unrecognized sub-option of -nullAnnotations: {0},\nlegal values are 'nullable=..', 'nonnull=..', 'emulate' and 'import'
configure.noClasspath = no classpath defined, using default directory instead
configure.incorrectClasspath = incorrect classpath: {0}
configure.invalidexpansionargumentname = expansion argument file {0} does not exist or cannot be read
@@ -222,6 +224,22 @@
\ -classNames \n\
\ qualified names of binary classes to process\n\
\ \n\
+\ Null annotation options:\n\
+\ -nullAnnotations:\n\
+\ enable use of annotations for specifying null contracts;\n\
+\ is a non-empty, comma-separated list of:\n\
+\ nullable=\n\
+\ specifies the fully qualified name of an annotation type\n\
+\ to be used for marking types whose values include null\n\
+\ nonnull=\n\
+\ specifies the fully qualified name of an annotation type\n\
+\ to be used for marking types whose values cannot be null\n\
+\ emulate tells the compiler to emulate the above annotation types\n\
+\ although they do not exist on the classpath\n\
+\ import tells the compiler to import the above annotation types\n\
+\ without specific mention in the sources such that their\n\
+\ simple names can be used without explicit imports\n\
+\ \n\
\ Advanced options:\n\
\ @ read command line arguments from file\n\
\ -maxProblems max number of problems per compilation unit (100 by\n\
Index: compiler/org/eclipse/jdt/core/compiler/IProblem.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java,v
retrieving revision 1.223
diff -u -r1.223 IProblem.java
--- compiler/org/eclipse/jdt/core/compiler/IProblem.java 17 Dec 2010 09:38:57 -0000 1.223
+++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 19 Dec 2010 20:25:04 -0000
@@ -116,7 +116,16 @@
* Benjamin Muskalla - added the following constants
* MissingSynchronizedModifierInInheritedMethod
* Stephan Herrmann - added the following constants
- * UnusedObjectAllocation
+ * UnusedObjectAllocation
+ * DefiniteNullFromNonNullMethod
+ * PotentialNullFromNonNullMethod
+ * NonNullReturnInsufficientInfo
+ * DefiniteNullToNonnullParameter
+ * PotentialNullToNonnullParameter
+ * NonNullParameterInsufficientInfo
+ * ConflictingTypeEmulation
+ * IllegalRedefinitionToNullableReturn
+ * IllegalRedefinitionToNonNullParameter
*******************************************************************************/
package org.eclipse.jdt.core.compiler;
@@ -1340,6 +1349,28 @@
int JavadocTypeArgumentsForRawGenericConstructor = Javadoc + Internal + 859;
/**
+ * Null Annotations
+ */
+ /** @since 3.7 */
+ int DefiniteNullFromNonNullMethod = MethodRelated + 880;
+ /** @since 3.7 */
+ int PotentialNullFromNonNullMethod = MethodRelated + 881;
+ /** @since 3.7 */
+ int NonNullReturnInsufficientInfo = MethodRelated + 882;
+ /** @since 3.7 */
+ int DefiniteNullToNonnullParameter = MethodRelated + 883;
+ /** @since 3.7 */
+ int PotentialNullToNonnullParameter = MethodRelated + 884;
+ /** @since 3.7 */
+ int NonNullParameterInsufficientInfo = MethodRelated + 885;
+ /** @since 3.7 */
+ int ConflictingTypeEmulation = ImportRelated + 886;
+ /** @since 3.7 */
+ int IllegalRedefinitionToNullableReturn = MethodRelated + 887;
+ /** @since 3.7 */
+ int IllegalRedefinitionToNonNullParameter = MethodRelated + 888;
+
+ /**
* External problems -- These are problems defined by other plugins
*/
Index: compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java,v
retrieving revision 1.110
diff -u -r1.110 AbstractMethodDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java 9 Nov 2010 19:59:19 -0000 1.110
+++ compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java 19 Dec 2010 20:25:04 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -100,6 +101,12 @@
} else if (paramAnnotations != null) {
paramAnnotations[i] = Binding.NO_ANNOTATIONS;
}
+ // transfer nullness info from the argument to the method:
+ if ((argument.binding.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0) {
+ if (this.binding.parameterNonNullness == null)
+ this.binding.parameterNonNullness = new Boolean[this.arguments.length];
+ this.binding.parameterNonNullness[i] = Boolean.valueOf((argument.binding.tagBits & TagBits.AnnotationNonNull) != 0);
+ }
}
if (paramAnnotations != null)
this.binding.setParameterAnnotations(paramAnnotations);
@@ -415,7 +422,6 @@
}
try {
- bindArguments();
bindThrownExceptions();
resolveJavadoc();
resolveAnnotations(this.scope, this.annotations, this.binding);
Index: compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java,v
retrieving revision 1.67
diff -u -r1.67 Annotation.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java 20 Jul 2010 20:23:19 -0000 1.67
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java 19 Dec 2010 20:25:05 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -161,6 +162,12 @@
case TypeIds.T_JavaLangSuppressWarnings :
tagBits |= TagBits.AnnotationSuppressWarnings;
break;
+ case TypeIds.T_ConfiguredAnnotationNullable :
+ tagBits |= TagBits.AnnotationNullable;
+ break;
+ case TypeIds.T_ConfiguredAnnotationNonNull :
+ tagBits |= TagBits.AnnotationNonNull;
+ break;
}
return tagBits;
}
Index: compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java,v
retrieving revision 1.149
diff -u -r1.149 MessageSend.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 17 Dec 2010 09:38:55 -0000 1.149
+++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 19 Dec 2010 20:25:06 -0000
@@ -8,7 +8,9 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
- * 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 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -80,6 +82,15 @@
this.arguments[i].checkNPE(currentScope, flowContext, flowInfo);
}
flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
+ // compare actual null-status against parameter annotations of the called method:
+ int nullStatus = this.arguments[i].nullStatus(flowInfo);
+ if (nullStatus != FlowInfo.NON_NULL
+ && this.binding.parameterNonNullness != null
+ && this.binding.parameterNonNullness[i].booleanValue()) // if @NonNull is required
+ {
+ char[][] annotationName = currentScope.environment().globalOptions.nonNullAnnotationName;
+ currentScope.problemReporter().possiblyNullToNonNullParameter(this.arguments[i], nullStatus, annotationName[annotationName.length-1]);
+ }
}
}
ReferenceBinding[] thrownExceptions;
@@ -250,6 +261,14 @@
}
}
public int nullStatus(FlowInfo flowInfo) {
+ 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;
+ if ((tagBits & TagBits.AnnotationNonNull) != 0)
+ return FlowInfo.NON_NULL;
+ if ((tagBits & TagBits.AnnotationNullable) != 0)
+ return FlowInfo.POTENTIALLY_NULL;
+ }
return FlowInfo.UNKNOWN;
}
Index: compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java,v
retrieving revision 1.77
diff -u -r1.77 MethodDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 17 Dec 2010 09:38:53 -0000 1.77
+++ compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 19 Dec 2010 20:25:06 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -89,6 +90,12 @@
if (this.binding != null && this.binding.declaringClass == declaringElement)
this.bits &= ~ASTNode.CanBeStatic;
}
+ // leverage null-info from parameter annotations:
+ long argumentTagBits = this.arguments[i].binding.tagBits;
+ if ((argumentTagBits & TagBits.AnnotationNullable) != 0)
+ flowInfo.markPotentiallyNullBit(this.arguments[i].binding);
+ else if ((argumentTagBits & TagBits.AnnotationNonNull) != 0)
+ flowInfo.markAsDefinitelyNonNull(this.arguments[i].binding);
}
}
if (this.binding.declaringClass instanceof MemberTypeBinding && !this.binding.declaringClass.isStatic()) {
Index: compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java,v
retrieving revision 1.68
diff -u -r1.68 ReturnStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java 12 Aug 2010 16:58:28 -0000 1.68
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java 19 Dec 2010 20:25:08 -0000
@@ -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 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -40,6 +42,18 @@
if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) {
this.expression.checkNPE(currentScope, flowContext, flowInfo);
}
+ if (this.expression.nullStatus(flowInfo) != FlowInfo.NON_NULL) {
+ // if we can't prove non-null check against declared null-ness of the enclosing method:
+ AbstractMethodDeclaration referenceMethod = currentScope.methodScope().referenceMethod();
+ if (referenceMethod != null) {
+ MethodBinding method = referenceMethod.binding;
+ if ((method.tagBits & TagBits.AnnotationNonNull) != 0) {
+ char[][] annotationName = currentScope.environment().globalOptions.nonNullAnnotationName;
+ currentScope.problemReporter().possiblyNullFromNonNullMethod(this, this.expression.nullStatus(flowInfo),
+ annotationName[annotationName.length-1]);
+ }
+ }
+ }
}
this.initStateIndex =
currentScope.methodScope().recordInitializationStates(flowInfo);
Index: compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java,v
retrieving revision 1.237
diff -u -r1.237 CompilerOptions.java
--- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 17 Dec 2010 09:38:52 -0000 1.237
+++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 19 Dec 2010 20:25:12 -0000
@@ -10,6 +10,7 @@
* Benjamin Muskalla - Contribution for bug 239066
* Stephan Herrmann - Contribution for bug 236385
* Stephan Herrmann - Contribution for bug 295551
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.impl;
@@ -106,6 +107,9 @@
public static final String OPTION_ReportNullReference = "org.eclipse.jdt.core.compiler.problem.nullReference"; //$NON-NLS-1$
public static final String OPTION_ReportPotentialNullReference = "org.eclipse.jdt.core.compiler.problem.potentialNullReference"; //$NON-NLS-1$
public static final String OPTION_ReportRedundantNullCheck = "org.eclipse.jdt.core.compiler.problem.redundantNullCheck"; //$NON-NLS-1$
+ public static final String OPTION_ReportNullContractViolation = "org.eclipse.jdt.core.compiler.problem.nullContractViolation"; //$NON-NLS-1$
+ public static final String OPTION_ReportPotentialNullContractViolation = "org.eclipse.jdt.core.compiler.problem.potentialNullContractViolation"; //$NON-NLS-1$
+ public static final String OPTION_ReportNullContractInsufficientInfo = "org.eclipse.jdt.core.compiler.problem.nullContractInsufficientInfo"; //$NON-NLS-1$
public static final String OPTION_ReportAutoboxing = "org.eclipse.jdt.core.compiler.problem.autoboxing"; //$NON-NLS-1$
public static final String OPTION_ReportAnnotationSuperInterface = "org.eclipse.jdt.core.compiler.problem.annotationSuperInterface"; //$NON-NLS-1$
public static final String OPTION_ReportMissingOverrideAnnotation = "org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation"; //$NON-NLS-1$
@@ -141,6 +145,11 @@
public static final String OPTION_ReportMissingAnnotation = "org.eclipse.jdt.core.compiler.problem.missingAnnotation"; //$NON-NLS-1$
public static final String OPTION_ReportMissingJavadoc = "org.eclipse.jdt.core.compiler.problem.missingJavadoc"; //$NON-NLS-1$
+ public static final String OPTION_NullableAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nullable"; //$NON-NLS-1$
+ public static final String OPTION_NonNullAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nonnull"; //$NON-NLS-1$
+ public static final String OPTION_EmulateNullAnnotationTypes = "org.eclipse.jdt.core.compiler.annotation.emulate"; //$NON-NLS-1$
+ public static final String OPTION_DefaultImportNullAnnotationTypes = "org.eclipse.jdt.core.compiler.annotation.defaultImport"; //$NON-NLS-1$
+
/**
* Possible values for configurable options
*/
@@ -170,6 +179,9 @@
public static final String NO_TAG = "no_tag"; //$NON-NLS-1$
public static final String ALL_STANDARD_TAGS = "all_standard_tags"; //$NON-NLS-1$
+ private static final char[][] DEFAULT_NONNULL_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNull".toCharArray()); //$NON-NLS-1$
+ private static final char[][] DEFAULT_NULLABLE_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.Nullable".toCharArray()); //$NON-NLS-1$
+
/**
* Bit mask for configurable problems (error/warning threshold)
* Note: bitmask assumes 3 highest bits to denote irritant group (to allow storing 8 groups of 29 bits each
@@ -243,6 +255,9 @@
public static final int UnusedObjectAllocation = IrritantSet.GROUP2 | ASTNode.Bit4;
public static final int MethodCanBeStatic = IrritantSet.GROUP2 | ASTNode.Bit5;
public static final int MethodCanBePotentiallyStatic = IrritantSet.GROUP2 | ASTNode.Bit6;
+ public static final int NullContractViolation = IrritantSet.GROUP2 | ASTNode.Bit7;
+ public static final int PotentialNullContractViolation = IrritantSet.GROUP2 | ASTNode.Bit8;
+ public static final int NullContractInsufficientInfo = IrritantSet.GROUP2 | ASTNode.Bit9;
// Severity level for handlers
/**
@@ -364,6 +379,14 @@
public boolean includeNullInfoFromAsserts;
/** Controls whether forced generic type problems get reported */
public boolean reportUnavoidableGenericTypeProblems;
+ /** Fully qualified name of annotation to use as marker for nullable types. */
+ public char[][] nullableAnnotationName;
+ /** Fully qualified name of annotation to use as marker for nonnull types. */
+ public char[][] nonNullAnnotationName;
+ /** Should null annotation types be emulated by synthetic bindings? */
+ public boolean emulateNullAnnotationTypes;
+ /** Should null annotation types be imported by default? */
+ public boolean defaultImportNullAnnotationTypes;
// keep in sync with warningTokenToIrritant and warningTokenFromIrritant
public final static String[] warningTokens = {
@@ -503,6 +526,12 @@
return OPTION_ReportPotentialNullReference;
case RedundantNullCheck :
return OPTION_ReportRedundantNullCheck;
+ case NullContractViolation :
+ return OPTION_ReportNullContractViolation;
+ case PotentialNullContractViolation :
+ return OPTION_ReportPotentialNullContractViolation;
+ case NullContractInsufficientInfo :
+ return OPTION_ReportNullContractInsufficientInfo;
case AutoBoxing :
return OPTION_ReportAutoboxing;
case AnnotationSuperInterface :
@@ -748,6 +777,9 @@
case NullReference :
case PotentialNullReference :
case RedundantNullCheck :
+ case NullContractViolation :
+ case PotentialNullContractViolation :
+ case NullContractInsufficientInfo :
return "null"; //$NON-NLS-1$
case FallthroughCase :
return "fallthrough"; //$NON-NLS-1$
@@ -923,6 +955,9 @@
optionsMap.put(OPTION_ReportNullReference, getSeverityString(NullReference));
optionsMap.put(OPTION_ReportPotentialNullReference, getSeverityString(PotentialNullReference));
optionsMap.put(OPTION_ReportRedundantNullCheck, getSeverityString(RedundantNullCheck));
+ optionsMap.put(OPTION_ReportNullContractViolation, getSeverityString(NullContractViolation));
+ optionsMap.put(OPTION_ReportPotentialNullContractViolation, getSeverityString(PotentialNullContractViolation));
+ optionsMap.put(OPTION_ReportNullContractInsufficientInfo, getSeverityString(NullContractInsufficientInfo));
optionsMap.put(OPTION_SuppressWarnings, this.suppressWarnings ? ENABLED : DISABLED);
optionsMap.put(OPTION_SuppressOptionalErrors, this.suppressOptionalErrors ? ENABLED : DISABLED);
optionsMap.put(OPTION_ReportUnhandledWarningToken, getSeverityString(UnhandledWarningToken));
@@ -943,6 +978,16 @@
optionsMap.put(OPTION_IncludeNullInfoFromAsserts, this.includeNullInfoFromAsserts ? ENABLED : DISABLED);
optionsMap.put(OPTION_ReportMethodCanBeStatic, getSeverityString(MethodCanBeStatic));
optionsMap.put(OPTION_ReportMethodCanBePotentiallyStatic, getSeverityString(MethodCanBePotentiallyStatic));
+ if (this.nullableAnnotationName != null) {
+ char[] compoundName = CharOperation.concatWith(this.nullableAnnotationName, '.');
+ optionsMap.put(OPTION_NullableAnnotationName, String.valueOf(compoundName));
+ }
+ if (this.nonNullAnnotationName != null) {
+ char[] compoundName = CharOperation.concatWith(this.nonNullAnnotationName, '.');
+ optionsMap.put(OPTION_NonNullAnnotationName, String.valueOf(compoundName));
+ }
+ optionsMap.put(OPTION_EmulateNullAnnotationTypes, this.emulateNullAnnotationTypes ? ENABLED : DISABLED);
+ optionsMap.put(OPTION_DefaultImportNullAnnotationTypes, this.defaultImportNullAnnotationTypes ? ENABLED : DISABLED);
return optionsMap;
}
@@ -1351,6 +1396,9 @@
if ((optionValue = optionsMap.get(OPTION_ReportNullReference)) != null) updateSeverity(NullReference, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportPotentialNullReference)) != null) updateSeverity(PotentialNullReference, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportRedundantNullCheck)) != null) updateSeverity(RedundantNullCheck, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_ReportNullContractViolation)) != null) updateSeverity(NullContractViolation, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_ReportPotentialNullContractViolation)) != null) updateSeverity(PotentialNullContractViolation, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_ReportNullContractInsufficientInfo)) != null) updateSeverity(NullContractInsufficientInfo, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportAutoboxing)) != null) updateSeverity(AutoBoxing, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportAnnotationSuperInterface)) != null) updateSeverity(AnnotationSuperInterface, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportMissingOverrideAnnotation)) != null) updateSeverity(MissingOverrideAnnotation, optionValue);
@@ -1485,6 +1533,36 @@
this.storeAnnotations = false;
}
}
+ if ((optionValue = optionsMap.get(OPTION_NullableAnnotationName)) != null) {
+ this.nullableAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
+ }
+ if ((optionValue = optionsMap.get(OPTION_NonNullAnnotationName)) != null) {
+ this.nonNullAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
+ }
+ if ((optionValue = optionsMap.get(OPTION_EmulateNullAnnotationTypes)) != null) {
+ if (ENABLED.equals(optionValue)) {
+ this.emulateNullAnnotationTypes = true;
+ // ensure that we actually have annotation names to emulate:
+ if (this.nullableAnnotationName == null)
+ this.nullableAnnotationName = DEFAULT_NULLABLE_ANNOTATION_NAME;
+ if (this.nonNullAnnotationName == null)
+ this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME;
+ } else if (DISABLED.equals(optionValue)) {
+ this.emulateNullAnnotationTypes = false;
+ }
+ }
+ if ((optionValue = optionsMap.get(OPTION_DefaultImportNullAnnotationTypes)) != null) {
+ if (ENABLED.equals(optionValue)) {
+ this.defaultImportNullAnnotationTypes = true;
+ // ensure that we actually have annotation names to be used for default imports:
+ if (this.nullableAnnotationName == null)
+ this.nullableAnnotationName = DEFAULT_NULLABLE_ANNOTATION_NAME;
+ if (this.nonNullAnnotationName == null)
+ this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME;
+ } else if (DISABLED.equals(optionValue)) {
+ this.defaultImportNullAnnotationTypes = false;
+ }
+ }
}
public String toString() {
StringBuffer buf = new StringBuffer("CompilerOptions:"); //$NON-NLS-1$
@@ -1560,6 +1638,9 @@
buf.append("\n\t- null reference: ").append(getSeverityString(NullReference)); //$NON-NLS-1$
buf.append("\n\t- potential null reference: ").append(getSeverityString(PotentialNullReference)); //$NON-NLS-1$
buf.append("\n\t- redundant null check: ").append(getSeverityString(RedundantNullCheck)); //$NON-NLS-1$
+ buf.append("\n\t- null contract violation: ").append(getSeverityString(NullContractViolation)); //$NON-NLS-1$
+ buf.append("\n\t- potential null contract vialotation: ").append(getSeverityString(PotentialNullContractViolation)); //$NON-NLS-1$
+ buf.append("\n\t- insufficient information for checking null contract: ").append(getSeverityString(NullContractInsufficientInfo)); //$NON-NLS-1$
buf.append("\n\t- autoboxing: ").append(getSeverityString(AutoBoxing)); //$NON-NLS-1$
buf.append("\n\t- annotation super interface: ").append(getSeverityString(AnnotationSuperInterface)); //$NON-NLS-1$
buf.append("\n\t- missing @Override annotation: ").append(getSeverityString(MissingOverrideAnnotation)); //$NON-NLS-1$
Index: compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java,v
retrieving revision 1.11
diff -u -r1.11 IrritantSet.java
--- compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java 17 Dec 2010 09:38:53 -0000 1.11
+++ compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java 19 Dec 2010 20:25:12 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.impl;
@@ -58,9 +59,11 @@
public static final IrritantSet UNCHECKED = new IrritantSet(CompilerOptions.UncheckedTypeOperation);
public static final IrritantSet UNQUALIFIED_FIELD_ACCESS = new IrritantSet(CompilerOptions.UnqualifiedFieldAccess);
- public static final IrritantSet COMPILER_DEFAULT_ERRORS = new IrritantSet(0); // no optional error by default
+ public static final IrritantSet COMPILER_DEFAULT_ERRORS = new IrritantSet(0); // see static initializer below
public static final IrritantSet COMPILER_DEFAULT_WARNINGS = new IrritantSet(0); // see static initializer below
static {
+ COMPILER_DEFAULT_ERRORS
+ .set(CompilerOptions.NullContractViolation);
COMPILER_DEFAULT_WARNINGS
// group-0 warnings enabled by default
.set(
@@ -98,7 +101,9 @@
// group-2 warnings enabled by default
.set(
CompilerOptions.DeadCode
- |CompilerOptions.Tasks);
+ | CompilerOptions.Tasks
+ | CompilerOptions.PotentialNullContractViolation
+ | CompilerOptions.NullContractInsufficientInfo);
ALL.setAll();
HIDING
@@ -107,7 +112,10 @@
.set(CompilerOptions.TypeHiding);
NULL
.set(CompilerOptions.PotentialNullReference)
- .set(CompilerOptions.RedundantNullCheck);
+ .set(CompilerOptions.RedundantNullCheck)
+ .set(CompilerOptions.NullContractViolation)
+ .set(CompilerOptions.PotentialNullContractViolation)
+ .set(CompilerOptions.NullContractInsufficientInfo);
RESTRICTION.set(CompilerOptions.DiscouragedReference);
STATIC_ACCESS.set(CompilerOptions.NonStaticAccessToStatic);
UNUSED
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java,v
retrieving revision 1.131
diff -u -r1.131 BinaryTypeBinding.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 19 Nov 2010 14:22:00 -0000 1.131
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 19 Dec 2010 20:25:15 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -553,6 +554,8 @@
isAnnotationType() ? convertMemberValue(method.getDefaultValue(), this.environment, missingTypeNames) : null,
this.environment);
+ scanForNullAnnotation(method, result);
+
if (use15specifics)
result.tagBits |= method.getTagBits();
result.typeVariables = typeVars;
@@ -1086,6 +1089,53 @@
AnnotationBinding[] retrieveAnnotations(Binding binding) {
return AnnotationBinding.addStandardAnnotations(super.retrieveAnnotations(binding), binding.getAnnotationTagBits(), this.environment);
}
+private void scanForNullAnnotation(IBinaryMethod method, MethodBinding result) {
+ char[][] nullableAnnotationName = this.environment.globalOptions.nullableAnnotationName;
+ char[][] nonNullAnnotationName = this.environment.globalOptions.nonNullAnnotationName;
+ if (nullableAnnotationName == null || nonNullAnnotationName == null)
+ return; // not configured to use null annotations
+
+ IBinaryAnnotation[] annotations = method.getAnnotations();
+ if (annotations != null) {
+ for (int i = 0; i < annotations.length; i++) {
+ char[] annotationTypeName = annotations[i].getTypeName();
+ if (annotationTypeName[0] != 'L')
+ continue;
+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+ if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ result.tagBits |= TagBits.AnnotationNonNull;
+ return;
+ }
+ if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ result.tagBits |= TagBits.AnnotationNullable;
+ return;
+ }
+ }
+ }
+
+ for (int j = 0; j < result.parameters.length; j++) {
+ IBinaryAnnotation[] paramAnnotations = method.getParameterAnnotations(j);
+ if (paramAnnotations != null) {
+ for (int i = 0; i < paramAnnotations.length; i++) {
+ char[] annotationTypeName = paramAnnotations[i].getTypeName();
+ if (annotationTypeName[0] != 'L')
+ continue;
+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+ if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ if (result.parameterNonNullness == null)
+ result.parameterNonNullness = new Boolean[result.parameters.length];
+ result.parameterNonNullness[i] = Boolean.TRUE;
+ break;
+ } else if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ if (result.parameterNonNullness == null)
+ result.parameterNonNullness = new Boolean[result.parameters.length];
+ result.parameterNonNullness[i] = Boolean.FALSE;
+ break;
+ }
+ }
+ }
+ }
+}
SimpleLookupTable storedAnnotations(boolean forceInitialize) {
if (forceInitialize && this.storedAnnotations == null) {
if (!this.environment.globalOptions.storeAnnotations)
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java,v
retrieving revision 1.128
diff -u -r1.128 CompilationUnitScope.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java 12 Aug 2010 08:51:55 -0000 1.128
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java 19 Dec 2010 20:25:16 -0000
@@ -7,7 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Erling Ellingsen - patch for bug 125570
+ * Erling Ellingsen - patch for bug 125570
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -160,7 +161,10 @@
// allocate the import array, add java.lang.* by default
int numberOfStatements = this.referenceContext.imports.length;
- int numberOfImports = numberOfStatements + 1;
+ int numberOfDefaultImports = 1;
+ if (this.environment.globalOptions.defaultImportNullAnnotationTypes)
+ numberOfDefaultImports += 2;
+ int numberOfImports = numberOfStatements + numberOfDefaultImports;
for (int i = 0; i < numberOfStatements; i++) {
ImportReference importReference = this.referenceContext.imports[i];
if (((importReference.bits & ASTNode.OnDemand) != 0) && CharOperation.equals(TypeConstants.JAVA_LANG, importReference.tokens) && !importReference.isStatic()) {
@@ -169,8 +173,11 @@
}
}
ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
- resolvedImports[0] = getDefaultImports()[0];
- int index = 1;
+ ImportBinding[] defaultImports = getDefaultImports(); // consistent number of default imports is ensured in LookupEnvironment.makeNullAnnotationTypeImports()
+ for (int i = 0; i < numberOfDefaultImports; i++) {
+ resolvedImports[i] = defaultImports[i];
+ }
+ int index = numberOfDefaultImports;
nextImport : for (int i = 0; i < numberOfStatements; i++) {
ImportReference importReference = this.referenceContext.imports[i];
@@ -303,6 +310,13 @@
return; // can be called when a field constant is resolved before static imports
if (this.referenceContext.imports == null) {
this.typeOrPackageCache = new HashtableOfObject(1);
+ for (int i = 0; i < this.imports.length; i++) {
+ // cache default-imported null annotation types:
+ if (!this.imports[i].onDemand) {
+ char[][] importName = this.imports[i].compoundName;
+ this.typeOrPackageCache.put(importName[importName.length-1], this.imports[i].resolvedImport);
+ }
+ }
return;
}
@@ -327,9 +341,18 @@
break;
}
}
+ int numberOfDefaultImports = 1;
+ if (this.environment.globalOptions.defaultImportNullAnnotationTypes) {
+ numberOfDefaultImports += 2;
+ numberOfImports += 2;
+ }
ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
- resolvedImports[0] = getDefaultImports()[0];
- int index = 1;
+ ImportBinding[] defaultImports = getDefaultImports(); // consistent number of default imports is ensured in LookupEnvironment.makeNullAnnotationTypeImports()
+ for (int i = 0; i < numberOfDefaultImports; i++) {
+ resolvedImports[i] = defaultImports[i];
+ }
+
+ int index = numberOfDefaultImports;
// keep static imports with normal imports until there is a reason to split them up
// on demand imports continue to be packages & types. need to check on demand type imports for fields/methods
@@ -613,7 +636,19 @@
importBinding = missingObject.fPackage;
}
- return this.environment.defaultImports = new ImportBinding[] {new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null)};
+ ImportBinding javaLangImport = new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null);
+ ImportBinding[] nullAnnotationImports = this.environment.makeNullAnnotationTypeImports(); // trigger regardless of option below
+ if (this.environment.globalOptions.defaultImportNullAnnotationTypes) {
+ ImportBinding[] allDefaultImports = new ImportBinding[nullAnnotationImports.length+1];// java.lang.* + null-annotations
+ allDefaultImports[0] = javaLangImport;
+ System.arraycopy(nullAnnotationImports, 0,
+ allDefaultImports, 1,
+ nullAnnotationImports.length);
+ this.environment.defaultImports = allDefaultImports;
+ } else {
+ this.environment.defaultImports = new ImportBinding[] {javaLangImport};
+ }
+ return this.environment.defaultImports;
}
// NOT Public API
public final Binding getImport(char[][] compoundName, boolean onDemand, boolean isStaticImport) {
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java,v
retrieving revision 1.109
diff -u -r1.109 LookupEnvironment.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java 1 Nov 2010 14:15:47 -0000 1.109
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java 19 Dec 2010 20:25:19 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1314,6 +1315,63 @@
return this.nameEnvironment.isPackage(null, name);
return this.nameEnvironment.isPackage(compoundName, name);
}
+
+private ReferenceBinding makeNullAnnotationType(char[][] compoundName, int typeId) {
+ char[][] packageName = CharOperation.subarray(compoundName, 0, compoundName.length-1);
+ PackageBinding packageBinding = createPackage(packageName);
+ ReferenceBinding typeBinding = packageBinding.getType(compoundName[compoundName.length-1]);
+ if (typeBinding != null && typeBinding.isValidBinding())
+ this.problemReporter.conflictingTypeEmulation(compoundName); // does not return
+
+ BinaryTypeBinding emulatedType = new BinaryTypeBinding();
+ emulatedType.compoundName = compoundName;
+ emulatedType.modifiers = ClassFileConstants.AccAnnotation | ClassFileConstants.AccPublic;
+ emulatedType.fields = Binding.NO_FIELDS;
+ emulatedType.methods = Binding.NO_METHODS;
+ emulatedType.memberTypes = Binding.NO_MEMBER_TYPES;
+ emulatedType.superclass = getType(TypeConstants.JAVA_LANG_OBJECT);
+ emulatedType.superInterfaces = Binding.NO_SUPERINTERFACES;
+ emulatedType.fPackage = packageBinding;
+ emulatedType.typeVariables = Binding.NO_TYPE_VARIABLES;
+ emulatedType.tagBits = TagBits.AreFieldsComplete | TagBits.AreFieldsSorted
+ | TagBits.AreMethodsComplete | TagBits.AreMethodsSorted
+ | TagBits.HasNoMemberTypes | TagBits.TypeVariablesAreConnected
+ | TagBits.AnnotationClassRetention
+ | TagBits.AnnotationForMethod | TagBits.AnnotationForParameter ;
+ emulatedType.id = typeId;
+
+ packageBinding.addType(emulatedType);
+
+ return emulatedType;
+}
+
+protected ImportBinding[] makeNullAnnotationTypeImports() {
+ char[][] nullableAnnotationName = this.globalOptions.nullableAnnotationName;
+ char[][] nonNullAnnotationName = this.globalOptions.nonNullAnnotationName;
+ if (nullableAnnotationName == null || nonNullAnnotationName == null) {
+ if (this.globalOptions.emulateNullAnnotationTypes || this.globalOptions.defaultImportNullAnnotationTypes)
+ // shouldn't happen by construction of CompilerOptions.set(Map)
+ this.problemReporter.abortDueToInternalError("Inconsistent null annotation options"); //$NON-NLS-1$
+ return new ImportBinding[0];
+ }
+ // fetch annotation types for emulation and/or default import:
+ ReferenceBinding nullableAnnotationType = null;
+ ReferenceBinding nonNullAnnotationType = null;
+ if (this.globalOptions.emulateNullAnnotationTypes) {
+ nullableAnnotationType = makeNullAnnotationType(nullableAnnotationName, TypeIds.T_ConfiguredAnnotationNullable);
+ nonNullAnnotationType = makeNullAnnotationType(nonNullAnnotationName, TypeIds.T_ConfiguredAnnotationNonNull);
+ } else if (this.globalOptions.defaultImportNullAnnotationTypes) {
+ nullableAnnotationType = getType(nullableAnnotationName);
+ nonNullAnnotationType = getType(nonNullAnnotationName);
+ }
+ if (this.globalOptions.defaultImportNullAnnotationTypes)
+ return new ImportBinding[] {
+ new ImportBinding(nullableAnnotationName, false, nullableAnnotationType, null),
+ new ImportBinding(nonNullAnnotationName, false, nonNullAnnotationType, null)
+ };
+ return new ImportBinding[0];
+}
+
// The method verifier is lazily initialized to guarantee the receiver, the compiler & the oracle are ready.
public MethodVerifier methodVerifier() {
if (this.verifier == null)
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java,v
retrieving revision 1.127
diff -u -r1.127 MethodBinding.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java 7 Sep 2010 13:39:18 -0000 1.127
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java 19 Dec 2010 20:25:20 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -28,6 +29,7 @@
public char[] selector;
public TypeBinding returnType;
public TypeBinding[] parameters;
+ public Boolean[] parameterNonNullness; // TRUE means @NonNull declared, FALSE means @Nullable declared, null means nothing declared
public ReferenceBinding[] thrownExceptions;
public ReferenceBinding declaringClass;
public TypeVariableBinding[] typeVariables = Binding.NO_TYPE_VARIABLES;
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java,v
retrieving revision 1.114
diff -u -r1.114 MethodVerifier.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 17 Dec 2010 06:40:12 -0000 1.114
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 19 Dec 2010 20:25:23 -0000
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla - Contribution for bug 239066
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -152,6 +153,7 @@
// interface I { @Override Object clone(); } does not override Object#clone()
currentMethod.modifiers |= ExtraCompilerModifiers.AccOverriding;
}
+ checkNullContractCompatibility(currentMethod, inheritedMethod);
if (!areReturnTypesCompatible(currentMethod, inheritedMethod)
&& (currentMethod.returnType.tagBits & TagBits.HasMissingType) == 0) {
@@ -184,7 +186,9 @@
checkForBridgeMethod(currentMethod, inheritedMethod, allInheritedMethods);
}
}
-
+protected void checkNullContractCompatibility(MethodBinding currentMethod, MethodBinding inheritedMethod) {
+ // nothing to do here. Real action happens at 1.5+
+}
public void reportRawReferences(MethodBinding currentMethod, MethodBinding inheritedMethod) {
// nothing to do here. Real action happens at 1.5+
}
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java,v
retrieving revision 1.121
diff -u -r1.121 MethodVerifier15.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 17 Dec 2010 06:40:13 -0000 1.121
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 19 Dec 2010 20:25:24 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -407,6 +408,39 @@
return false;
}
+protected void checkNullContractCompatibility(MethodBinding currentMethod, MethodBinding inheritedMethod) {
+ if ((inheritedMethod.tagBits & TagBits.AnnotationNonNull) != 0) {
+ if ((currentMethod.tagBits & TagBits.AnnotationNullable) != 0) {
+ AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod();
+ this.type.scope.problemReporter().illegalRedefinitionToNullableReturn(methodDecl, inheritedMethod.declaringClass,
+ this.environment.globalOptions.nonNullAnnotationName);
+ }
+ }
+ if ((currentMethod.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0)
+ currentMethod.tagBits |= (inheritedMethod.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable));
+
+ if (inheritedMethod.parameterNonNullness != null) {
+ AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod();
+ for (int i = 0; i < inheritedMethod.parameterNonNullness.length; i++) {
+ if (inheritedMethod.parameterNonNullness[i] == Boolean.FALSE) { // promised to accept @Nullable
+ if (currentMethod.parameterNonNullness == null)
+ currentMethod.parameterNonNullness = new Boolean[currentMethod.parameters.length];
+ if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) {
+ this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter(methodDecl.arguments[i],
+ inheritedMethod.declaringClass,
+ this.environment.globalOptions.nullableAnnotationName);
+ continue;
+ }
+ }
+ if (currentMethod.parameterNonNullness[i] == null && inheritedMethod.parameterNonNullness[i] != null) {
+ currentMethod.parameterNonNullness[i] = inheritedMethod.parameterNonNullness[i];
+ methodDecl.arguments[i].binding.tagBits |= (currentMethod.parameterNonNullness[i].booleanValue())
+ ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
+ }
+ }
+ }
+}
+
void reportRawReferences() {
CompilerOptions compilerOptions = this.type.scope.compilerOptions();
if (compilerOptions.sourceLevel < ClassFileConstants.JDK1_5 // shouldn't whine at all
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java,v
retrieving revision 1.183
diff -u -r1.183 SourceTypeBinding.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 17 Dec 2010 06:40:13 -0000 1.183
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 19 Dec 2010 20:25:27 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1408,9 +1409,12 @@
}
}
// only assign parameters if no problems are found
- if (!foundArgProblem) {
+ if (foundArgProblem) {
+ methodDecl.binding = null;
+ } else {
method.parameters = newParameters;
}
+ methodDecl.bindArguments();
}
boolean foundReturnTypeProblem = false;
@@ -1453,7 +1457,6 @@
}
}
if (foundArgProblem) {
- methodDecl.binding = null;
method.parameters = Binding.NO_PARAMETERS; // see 107004
// nullify type parameter bindings as well as they have a backpointer to the method binding
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=81134)
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java,v
retrieving revision 1.45
diff -u -r1.45 TagBits.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java 17 Dec 2010 06:40:13 -0000 1.45
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java 19 Dec 2010 20:25:27 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -119,10 +120,13 @@
long AnnotationInherited = ASTNode.Bit49L;
long AnnotationOverride = ASTNode.Bit50L;
long AnnotationSuppressWarnings = ASTNode.Bit51L;
- long AllStandardAnnotationsMask = AnnotationTargetMASK | AnnotationRetentionMASK | AnnotationDeprecated | AnnotationDocumented | AnnotationInherited | AnnotationOverride | AnnotationSuppressWarnings;
+ long AnnotationNullable = ASTNode.Bit52L;
+ long AnnotationNonNull = ASTNode.Bit53L;
+ long AllStandardAnnotationsMask = AnnotationTargetMASK | AnnotationRetentionMASK | AnnotationDeprecated | AnnotationDocumented
+ | AnnotationInherited | AnnotationOverride | AnnotationSuppressWarnings | AnnotationNullable | AnnotationNonNull;
- long DefaultValueResolved = ASTNode.Bit52L;
+ long DefaultValueResolved = ASTNode.Bit54L;
// set when type contains non-private constructor(s)
- long HasNonPrivateConstructor = ASTNode.Bit53L;
+ long HasNonPrivateConstructor = ASTNode.Bit55L;
}
Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java,v
retrieving revision 1.34
diff -u -r1.34 TypeIds.java
--- compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java 7 Mar 2009 01:08:09 -0000 1.34
+++ compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java 19 Dec 2010 20:25:28 -0000
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for Bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -89,6 +90,9 @@
final int T_JavaUtilCollection = 59;
+ final int T_ConfiguredAnnotationNullable = 60;
+ final int T_ConfiguredAnnotationNonNull = 61;
+
final int NoId = Integer.MAX_VALUE;
public static final int IMPLICIT_CONVERSION_MASK = 0xFF;
Index: compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java,v
retrieving revision 1.428
diff -u -r1.428 ProblemReporter.java
--- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 17 Dec 2010 09:38:53 -0000 1.428
+++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 19 Dec 2010 20:25:41 -0000
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Benjamin Muskalla - Contribution for bug 239066
* Stephan Herrmann - Contribution for bug 236385
+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.problem;
@@ -81,6 +82,7 @@
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
@@ -293,6 +295,18 @@
case IProblem.NullLocalVariableInstanceofYieldsFalse:
return CompilerOptions.RedundantNullCheck;
+ case IProblem.DefiniteNullFromNonNullMethod:
+ case IProblem.DefiniteNullToNonnullParameter:
+ case IProblem.IllegalRedefinitionToNullableReturn:
+ case IProblem.IllegalRedefinitionToNonNullParameter:
+ return CompilerOptions.NullContractViolation;
+ case IProblem.PotentialNullFromNonNullMethod:
+ case IProblem.PotentialNullToNonnullParameter:
+ return CompilerOptions.PotentialNullContractViolation;
+ case IProblem.NonNullParameterInsufficientInfo:
+ case IProblem.NonNullReturnInsufficientInfo:
+ return CompilerOptions.NullContractInsufficientInfo;
+
case IProblem.BoxingConversion :
case IProblem.UnboxingConversion :
return CompilerOptions.AutoBoxing;
@@ -469,6 +483,9 @@
case CompilerOptions.NullReference :
case CompilerOptions.PotentialNullReference :
case CompilerOptions.RedundantNullCheck :
+ case CompilerOptions.NullContractViolation :
+ case CompilerOptions.PotentialNullContractViolation :
+ case CompilerOptions.NullContractInsufficientInfo :
case CompilerOptions.IncompleteEnumSwitch :
case CompilerOptions.FallthroughCase :
case CompilerOptions.OverridingMethodWithoutSuperInvocation :
@@ -530,6 +547,7 @@
switch (problemID) {
case IProblem.IsClassPathCorrect :
case IProblem.CorruptedSignature :
+ case IProblem.ConflictingTypeEmulation :
return CategorizedProblem.CAT_BUILDPATH;
default :
@@ -1331,6 +1349,15 @@
importRef.sourceStart,
importRef.sourceEnd);
}
+public void conflictingTypeEmulation(char[][] compoundName) {
+ String[] arguments = new String[] {CharOperation.toString(compoundName)};
+ this.handle(
+ IProblem.ConflictingTypeEmulation,
+ arguments,
+ arguments,
+ ProblemSeverities.Error | ProblemSeverities.Abort | ProblemSeverities.Fatal, // not configurable
+ 0, 0);
+}
public void constantOutOfRange(Literal literal, TypeBinding literalType) {
String[] arguments = new String[] {new String(literalType.readableName()), new String(literal.source())};
this.handle(
@@ -2437,6 +2464,22 @@
qualifiedTypeReference.sourceStart,
qualifiedTypeReference.sourceEnd);
}
+public void illegalRedefinitionToNonNullParameter(Argument argument, ReferenceBinding declaringClass, char[][] nullableAnnotationName) {
+ this.handle(
+ IProblem.IllegalRedefinitionToNonNullParameter,
+ new String[] { new String(declaringClass.readableName()), CharOperation.toString(nullableAnnotationName)},
+ new String[] { new String(declaringClass.shortReadableName()), new String(nullableAnnotationName[nullableAnnotationName.length-1])},
+ argument.sourceStart,
+ argument.sourceEnd);
+}
+public void illegalRedefinitionToNullableReturn(AbstractMethodDeclaration methodDecl, ReferenceBinding declaringClass, char[][] nonNullAnnotationName) {
+ this.handle(
+ IProblem.IllegalRedefinitionToNullableReturn,
+ new String[] { new String(declaringClass.readableName()), CharOperation.toString(nonNullAnnotationName)},
+ new String[] { new String(declaringClass.shortReadableName()), new String(nonNullAnnotationName[nonNullAnnotationName.length-1])},
+ methodDecl.sourceStart,
+ methodDecl.sourceEnd);
+}
public void illegalStaticModifierForMemberType(SourceTypeBinding type) {
String[] arguments = new String[] {new String(type.sourceName())};
this.handle(
@@ -6078,6 +6121,34 @@
caseStatement.sourceStart,
caseStatement.sourceEnd);
}
+public void possiblyNullFromNonNullMethod(ReturnStatement returnStatement, int nullStatus, char[] annotationName) {
+ int problemId = IProblem.NonNullReturnInsufficientInfo;
+ if ((nullStatus & FlowInfo.NULL) != 0)
+ problemId = IProblem.DefiniteNullFromNonNullMethod;
+ if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
+ problemId = IProblem.PotentialNullFromNonNullMethod;
+ String[] arguments = new String[] { String.valueOf(annotationName) };
+ this.handle(
+ problemId,
+ arguments,
+ arguments,
+ returnStatement.sourceStart,
+ returnStatement.sourceEnd);
+}
+public void possiblyNullToNonNullParameter(Expression argument, int nullStatus, char[] annotationName) {
+ int problemId = IProblem.NonNullParameterInsufficientInfo;
+ if ((nullStatus & FlowInfo.NULL) != 0)
+ problemId = IProblem.DefiniteNullToNonnullParameter;
+ else if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
+ problemId = IProblem.PotentialNullToNonnullParameter;
+ String[] arguments = new String[] { String.valueOf(annotationName) };
+ this.handle(
+ problemId,
+ arguments,
+ arguments,
+ argument.sourceStart,
+ argument.sourceEnd);
+}
public void publicClassMustMatchFileName(CompilationUnitDeclaration compUnitDecl, TypeDeclaration typeDecl) {
this.referenceContext = typeDecl; // report the problem against the type not the entire compilation unit
String[] arguments = new String[] {new String(compUnitDecl.getFileName()), new String(typeDecl.name)};
Index: compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties,v
retrieving revision 1.260
diff -u -r1.260 messages.properties
--- compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 17 Dec 2010 09:38:53 -0000 1.260
+++ compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 19 Dec 2010 20:25:42 -0000
@@ -8,7 +8,9 @@
# Contributors:
# IBM Corporation - initial API and implementation
# Benjamin Muskalla - Contribution for bug 239066
-# Stephan Herrmann - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
+# Stephan Herrmann - Contributions for
+# bug 185682 - Increment/decrement operators mark local variables as read
+# bug 186342 - [compiler][null]Using annotations for null checking
###############################################################################
0 = {0}
1 = super cannot be used in java.lang.Object
@@ -619,6 +621,17 @@
858 = The parameterized constructor <{3}>{0}({1}) of type {2} is not applicable for the arguments ({4})
859 = The constructor {0}({1}) of raw type {2} is no longer generic; it cannot be parameterized with arguments <{3}>
+### NULL ANNOTATIONS
+880 = Null contract violation: returning null from a method declared as @{0}.
+881 = Potential null contract violation: return value can be null but method is declared as @{0}.
+882 = Potential null contract violation: insufficient nullness information regarding return value while the method is declared as @{0}.
+883 = Null contract violation: passing null to a parameter declared as @{0}.
+884 = Potential null contract violation: potentially passing null to a parameter declared as @{0}.
+885 = Potential null contract violation: insufficient nullness information regarding a value that is passed to a parameter declared as @{0}.
+886 = Buildpath problem: emulation of type {0} is requested (for null annotations) but a type of this name exists on the build path.
+887 = Cannot relax null contract for method return, inherited method from {0} is declared as @{1}.
+888 = Cannot tighten null contract for parameter, inherited method from {0} declares the parameter as @{1}.
+
### ELABORATIONS
## Access restrictions
78592 = The type {1} is not accessible due to restriction on classpath entry {0}
Index: model/org/eclipse/jdt/core/JavaCore.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java,v
retrieving revision 1.657
diff -u -r1.657 JavaCore.java
--- model/org/eclipse/jdt/core/JavaCore.java 17 Dec 2010 09:38:57 -0000 1.657
+++ model/org/eclipse/jdt/core/JavaCore.java 19 Dec 2010 20:25:52 -0000
@@ -83,6 +83,9 @@
* Benjamin Muskalla - added COMPILER_PB_MISSING_SYNCHRONIZED_ON_INHERITED_METHOD
* Stephan Herrmann - added COMPILER_PB_UNUSED_OBJECT_ALLOCATION
* Stephan Herrmann - added COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS
+ * Stephan Herrmann - added COMPILER_PB_NULL_CONTRACT_VIOLATION
+ * COMPILER_PB_POTENTIAL_NULL_CONTRACT_VIOLATION
+ * COMPILER_PB_NULL_CONTRACT_INSUFFICIENT_INFO
*******************************************************************************/
package org.eclipse.jdt.core;
@@ -1576,6 +1579,80 @@
*/
public static final String COMPILER_PB_POTENTIAL_NULL_REFERENCE = PLUGIN_ID + ".compiler.problem.potentialNullReference"; //$NON-NLS-1$
/**
+ * Compiler option ID: Reporting Violations of Null Contracts.
+ *
When enabled, the compiler will issue an error or a warning whenever one of the
+ * following situations is detected:
+ *
+ *
a method declared with a nonnull annotation returns an expression that is
+ * statically known to evaluate to a null value
+ *
an expression that is statically known to evaluate to a null value is passed
+ * as an argument in a method call where the corresponding parameter of the called
+ * method is declared with a nonnull annotation.
+ *
a method that overrides an inherited method declared with a nonnull annotation
+ * tries to relax that contract by specifying a nullable annotation
+ * (prohibition of contravariant return).
+ *
a method that overrides an inherited method which has a nullable declaration
+ * for at least one of its parameters, tries to tighten that null contract by
+ * specifying a nonnull annotation for its corresponding parameter
+ * (prohibition of covariant parameters).
When enabled, the compiler will issue an error or a warning whenever one of the
+ * following situations is detected:
+ *
+ *
a method declared with a nonnull annotation returns an expression that is
+ * statically known to evaluate to a null value on some flow
+ *
an expression that is statically known to evaluate to a null value on some flow
+ * is passed as an argument in a method call where the corresponding parameter of
+ * the called method is declared with a nonnull annotation.
+ * @since 3.7
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_PB_POTENTIAL_NULL_CONTRACT_VIOLATION = PLUGIN_ID + ".compiler.problem.potentialNullContractViolation"; //$NON-NLS-1$
+ /**
+ * Compiler option ID: Reporting Insufficient Information for Analysing Adherence to Null Contracts.
+ *
When enabled, the compiler will issue an error or a warning whenever one of the
+ * following situations is detected:
+ *
+ *
a method declared with a nonnull annotation returns an expression for which
+ * insufficient nullness information is available for statically proving that no
+ * flow will pass a null value at runtime.
+ *
an expression for which insufficient nullness information is available for
+ * statically proving that it will never evaluate to a null value at runtime
+ * is passed as an argument in a method call where the corresponding parameter of
+ * the called method is declared with a nonnull annotation.
+ *
+ * Insufficient nullness information is usually a consequence of using other unannotated
+ * variables or methods.
+ *
+ *
When enabled, the compiler will issue an error or a warning whenever a
* variable that is statically known to hold a null or a non-null value
#P org.eclipse.jdt.core.tests.compiler
Index: src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java,v
retrieving revision 1.222
diff -u -r1.222 BatchCompilerTest.java
--- src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 17 Dec 2010 09:39:05 -0000 1.222
+++ src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 19 Dec 2010 20:26:20 -0000
@@ -10,7 +10,9 @@
* Benjamin Muskalla - Contribution for bug 239066
* Stephan Herrmann - Contribution for bug 236385
* Stephan Herrmann - Contribution for bug 295551
- * Stephan Herrmann - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
+ * Stephan Herrmann - Contributions for
+ * bug 185682 - Increment/decrement operators mark local variables as read
+ * bug 186342 - [compiler][null]Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -48,7 +50,7 @@
private static final Main MAIN = new Main(null/*outWriter*/, null/*errWriter*/, false/*systemExit*/, null/*options*/, null/*progress*/);
static {
-// TESTS_NAMES = new String[] { "test295_warn_options" };
+// TESTS_NAMES = new String[] { "testNullAnnotations" };
// TESTS_NUMBERS = new int[] { 306 };
// TESTS_RANGE = new int[] { 298, -1 };
}
@@ -1589,6 +1591,22 @@
" -classNames \n" +
" qualified names of binary classes to process\n" +
" \n" +
+ " Null annotation options:\n" +
+ " -nullAnnotations:\n" +
+ " enable use of annotations for specifying null contracts;\n" +
+ " is a non-empty, comma-separated list of:\n" +
+ " nullable=\n" +
+ " specifies the fully qualified name of an annotation type\n" +
+ " to be used for marking types whose values include null\n" +
+ " nonnull=\n" +
+ " specifies the fully qualified name of an annotation type\n" +
+ " to be used for marking types whose values cannot be null\n" +
+ " emulate tells the compiler to emulate the above annotation types\n" +
+ " although they do not exist on the classpath\n" +
+ " import tells the compiler to import the above annotation types\n" +
+ " without specific mention in the sources such that their\n" +
+ " simple names can be used without explicit imports\n" +
+ " \n" +
" Advanced options:\n" +
" @ read command line arguments from file\n" +
" -maxProblems max number of problems per compilation unit (100 by\n" +
@@ -1793,6 +1811,8 @@
" \n" +
" \n" +
" \n" +
+ " \n" +
@@ -1850,11 +1870,14 @@
"