### 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 15 Jan 2011 17:19:20 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -10,8 +10,10 @@ * Tom Tromey - Contribution for bug 125961 * Tom Tromey - Contribution for bug 159641 * Benjamin Muskalla - Contribution for bug 239066 - * Stephan Herrmann - Contribution for bug 236385 - * Stephan Herrmann - Contribution for bug 295551 + * Stephan Herrmann - Contributions for + * bug 236385 - [compiler] Warn for potential programming problem if an object is created but not used + * bug 295551 - Add option to automatically promote all warnings to errors + * bug 186342 - [compiler][null]Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler.batch; @@ -2388,6 +2390,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.951 diff -u -r1.951 messages.properties --- batch/org/eclipse/jdt/internal/compiler/batch/messages.properties 14 Jan 2011 17:02:25 -0000 1.951 +++ batch/org/eclipse/jdt/internal/compiler/batch/messages.properties 15 Jan 2011 17:19:22 -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 ############################################################################### ### 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 15 Jan 2011 17:19:24 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -116,7 +116,19 @@ * Benjamin Muskalla - added the following constants * MissingSynchronizedModifierInInheritedMethod * Stephan Herrmann - added the following constants - * UnusedObjectAllocation + * UnusedObjectAllocation + * DefiniteNullFromNonNullMethod + * PotentialNullFromNonNullMethod + * NonNullReturnInsufficientInfo + * DefiniteNullToNonNullParameter + * PotentialNullToNonNullParameter + * NonNullParameterInsufficientInfo + * DefiniteNullToNonNullLocal + * PotentialNullToNonNullLocal + * NonNullLocalInsufficientInfo + * ConflictingTypeEmulation + * IllegalRedefinitionToNullableReturn + * IllegalRedefinitionToNonNullParameter *******************************************************************************/ package org.eclipse.jdt.core.compiler; @@ -1340,6 +1352,38 @@ 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 DefiniteNullToNonNullLocal = Internal + 886; + /** @since 3.7 */ + int PotentialNullToNonNullLocal = Internal + 887; + /** @since 3.7 */ + int NonNullLocalInsufficientInfo = Internal + 888; + /** @since 3.7 */ + int ConflictingTypeEmulation = ImportRelated + 889; + /** @since 3.7 */ + int MissingNullAnnotationType = ImportRelated + 890; + /** @since 3.7 */ + int IllegalRedefinitionToNullableReturn = MethodRelated + 891; + /** @since 3.7 */ + int IllegalRedefinitionToNonNullParameter = MethodRelated + 892; + /** @since 3.7 */ + int IllegalDefinitionToNonNullParameter = MethodRelated + 893; + + /** * 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 15 Jan 2011 17:19:24 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:26 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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/Assignment.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java,v retrieving revision 1.92 diff -u -r1.92 Assignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 9 Sep 2010 17:36:20 -0000 1.92 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 15 Jan 2011 17:19:26 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -8,8 +8,10 @@ * Contributors: * IBM Corporation - initial API and implementation * Genady Beriozkin - added support for reporting assignment with no effect - * Stephan Herrmann - Contribution for bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE - * and bug 292478 - Report potentially null across variable assignment + * Stephan Herrmann - Contributions for + * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE + * bug 292478 - Report potentially null across variable assignment + * bug 186342 - [compiler][null]Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -52,6 +54,14 @@ flowInfo = ((Reference) this.lhs) .analyseAssignment(currentScope, flowContext, flowInfo, this, false) .unconditionalInits(); + if ( local != null + && (local.tagBits & TagBits.AnnotationNonNull) != 0 + && nullStatus != FlowInfo.NON_NULL) + { + currentScope.problemReporter().possiblyNullToNonNullLocal(local.name, this.expression, + nullStatus, currentScope.environment().globalOptions.nonNullAnnotationName); + nullStatus = FlowInfo.NON_NULL; // from now on assume we adhere to the contract + } if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { flowInfo.markNullStatus(local, nullStatus); if (flowContext.initsOnFinally != null) Index: compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java,v retrieving revision 1.75 diff -u -r1.75 LocalDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 17 Dec 2010 09:38:55 -0000 1.75 +++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 15 Jan 2011 17:19:27 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -7,8 +7,10 @@ * * Contributors: * IBM Corporation - initial API and implementation - * Stephan Herrmann - Contribution for bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE - * and bug 292478 - Report potentially null across variable assignment + * Stephan Herrmann - Contributions for + * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE + * bug 292478 - Report potentially null across variable assignment + * bug 186342 - [compiler][null]Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -80,6 +82,13 @@ this.bits &= ~FirstAssignmentToLocal; // int i = (i = 0); } flowInfo.markAsDefinitelyAssigned(this.binding); + if ( (this.binding.tagBits & TagBits.AnnotationNonNull) != 0 + && nullStatus != FlowInfo.NON_NULL) + { + currentScope.problemReporter().possiblyNullToNonNullLocal(this.name, this.initialization, + nullStatus, currentScope.environment().globalOptions.nonNullAnnotationName); + nullStatus = FlowInfo.NON_NULL; // from now on assume we adhere to the contract + } if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) { flowInfo.markNullStatus(this.binding, nullStatus); // no need to inform enclosing try block since its locals won't get 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 15 Jan 2011 17:19:27 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:29 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:29 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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.239 diff -u -r1.239 CompilerOptions.java --- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 13 Jan 2011 18:16:08 -0000 1.239 +++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 15 Jan 2011 17:19:34 -0000 @@ -8,8 +8,10 @@ * Contributors: * IBM Corporation - initial API and implementation * Benjamin Muskalla - Contribution for bug 239066 - * Stephan Herrmann - Contribution for bug 236385 - * Stephan Herrmann - Contribution for bug 295551 + * Stephan Herrmann - Contributions for + * bug 236385 - [compiler] Warn for potential programming problem if an object is created but not used + * bug 295551 - Add option to automatically promote all warnings to errors + * bug 186342 - [compiler][null]Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler.impl; @@ -106,6 +108,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$ @@ -136,6 +141,10 @@ public static final String OPTION_IncludeNullInfoFromAsserts = "org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts"; //$NON-NLS-1$ public static final String OPTION_ReportMethodCanBeStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic"; //$NON-NLS-1$ public static final String OPTION_ReportMethodCanBePotentiallyStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic"; //$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 */ @@ -165,6 +174,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 @@ -238,6 +250,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 /** @@ -359,6 +374,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 = { @@ -498,6 +521,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 : @@ -769,6 +798,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$ @@ -944,6 +976,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)); @@ -964,6 +999,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; } @@ -1372,6 +1417,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); @@ -1506,6 +1554,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$ @@ -1581,6 +1659,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 15 Jan 2011 17:19:34 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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,12 @@ 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 + | CompilerOptions.PotentialNullContractViolation); COMPILER_DEFAULT_WARNINGS // group-0 warnings enabled by default .set( @@ -98,7 +102,8 @@ // group-2 warnings enabled by default .set( CompilerOptions.DeadCode - |CompilerOptions.Tasks); + | CompilerOptions.Tasks + | 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.133 diff -u -r1.133 BinaryTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 6 Jan 2011 19:58:11 -0000 1.133 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 15 Jan 2011 17:19:35 -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; @@ -1092,6 +1095,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[j] = Boolean.TRUE; + break; + } else if (CharOperation.equals(typeName, nullableAnnotationName)) { + if (result.parameterNonNullness == null) + result.parameterNonNullness = new Boolean[result.parameters.length]; + result.parameterNonNullness[j] = 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 15 Jan 2011 17:19:38 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:41 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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,74 @@ 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.sourceName = compoundName[compoundName.length-1]; + 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 + | TagBits.AnnotationForLocalVariable ; + 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 { // not emulated means those types should exist (and need to be marked): + nullableAnnotationType = getType(nullableAnnotationName); + if (nullableAnnotationType != null && nullableAnnotationType.isValidBinding()) + nullableAnnotationType.id = TypeIds.T_ConfiguredAnnotationNullable; + else if (this.globalOptions.defaultImportNullAnnotationTypes) + this.problemReporter.missingNullAnnotationType(nullableAnnotationName); + + nonNullAnnotationType = getType(nonNullAnnotationName); + if (nonNullAnnotationType != null && nonNullAnnotationType.isValidBinding()) + nonNullAnnotationType.id = TypeIds.T_ConfiguredAnnotationNonNull; + else if (this.globalOptions.defaultImportNullAnnotationTypes) + this.problemReporter.missingNullAnnotationType(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 15 Jan 2011 17:19:42 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:46 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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,62 @@ return false; } +protected void checkNullContractCompatibility(MethodBinding currentMethod, MethodBinding inheritedMethod) { + // return type: + 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)); + + // parameters: + if (inheritedMethod.parameterNonNullness != null) { + // inherited method has null-annotations, check and possibly transfer: + + // prepare for transfering (contract inheritance): + if (currentMethod.parameterNonNullness == null) + currentMethod.parameterNonNullness = new Boolean[currentMethod.parameters.length]; + + for (int i = 0; i < inheritedMethod.parameterNonNullness.length; i++) { + + Boolean inheritedNonNullNess = inheritedMethod.parameterNonNullness[i]; + if (inheritedNonNullNess != Boolean.TRUE) { // super parameter is not restricted to @NonNull + if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) { // current parameter is restricted to @NonNull + this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter( + currentMethod.sourceMethod().arguments[i], + inheritedMethod.declaringClass, + inheritedNonNullNess == null + ? null + : this.environment.globalOptions.nullableAnnotationName); + continue; + } + } + + if (currentMethod.parameterNonNullness[i] == null && inheritedNonNullNess != null) { + // inherit this annotation as the current method has no annotation: + currentMethod.parameterNonNullness[i] = inheritedNonNullNess; + VariableBinding argumentBinding = currentMethod.sourceMethod().arguments[i].binding; + argumentBinding.tagBits |= inheritedNonNullNess.booleanValue() + ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable; + } + } + } else if (currentMethod.parameterNonNullness != null) { + // super method has no annotations but current has + for (int i = 0; i < currentMethod.parameterNonNullness.length; i++) { + if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) { // tightening from unconstrained to @NonNull + this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter( + currentMethod.sourceMethod().arguments[i], + inheritedMethod.declaringClass, + null); + } + } + } +} + 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.184 diff -u -r1.184 SourceTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 5 Jan 2011 19:57:26 -0000 1.184 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 15 Jan 2011 17:19:49 -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; @@ -1420,9 +1421,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; @@ -1465,7 +1469,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 15 Jan 2011 17:19:49 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:19:49 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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 15 Jan 2011 17:20:03 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -8,7 +8,9 @@ * Contributors: * IBM Corporation - initial API and implementation * Benjamin Muskalla - Contribution for bug 239066 - * Stephan Herrmann - Contribution for bug 236385 + * Stephan Herrmann - Contributions for + * bug 236385 - [compiler] Warn for potential programming problem if an object is created but not used + * bug 186342 - [compiler][null]Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler.problem; @@ -81,6 +83,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 +296,22 @@ case IProblem.NullLocalVariableInstanceofYieldsFalse: return CompilerOptions.RedundantNullCheck; + case IProblem.DefiniteNullFromNonNullMethod: + case IProblem.DefiniteNullToNonNullLocal: + case IProblem.DefiniteNullToNonNullParameter: + case IProblem.IllegalRedefinitionToNullableReturn: + case IProblem.IllegalRedefinitionToNonNullParameter: + case IProblem.IllegalDefinitionToNonNullParameter: + return CompilerOptions.NullContractViolation; + case IProblem.PotentialNullFromNonNullMethod: + case IProblem.PotentialNullToNonNullLocal: + case IProblem.PotentialNullToNonNullParameter: + return CompilerOptions.PotentialNullContractViolation; + case IProblem.NonNullLocalInsufficientInfo: + case IProblem.NonNullParameterInsufficientInfo: + case IProblem.NonNullReturnInsufficientInfo: + return CompilerOptions.NullContractInsufficientInfo; + case IProblem.BoxingConversion : case IProblem.UnboxingConversion : return CompilerOptions.AutoBoxing; @@ -469,6 +488,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 +552,8 @@ switch (problemID) { case IProblem.IsClassPathCorrect : case IProblem.CorruptedSignature : + case IProblem.ConflictingTypeEmulation : + case IProblem.MissingNullAnnotationType : return CategorizedProblem.CAT_BUILDPATH; default : @@ -1331,6 +1355,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 +2470,32 @@ qualifiedTypeReference.sourceStart, qualifiedTypeReference.sourceEnd); } +public void illegalRedefinitionToNonNullParameter(Argument argument, ReferenceBinding declaringClass, char[][] inheritedAnnotationName) { + if (inheritedAnnotationName == null) { + this.handle( + IProblem.IllegalDefinitionToNonNullParameter, + new String[] { new String(argument.name), new String(declaringClass.readableName()) }, + new String[] { new String(argument.name), new String(declaringClass.shortReadableName()) }, + argument.sourceStart, + argument.sourceEnd); + + } else { + this.handle( + IProblem.IllegalRedefinitionToNonNullParameter, + 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.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( @@ -5116,6 +5175,11 @@ switchStatement.expression.sourceStart, switchStatement.expression.sourceEnd); } + +public void missingNullAnnotationType(char[][] nullAnnotationName) { + String[] args = { new String(CharOperation.concatWith(nullAnnotationName, '.')) }; + this.handle(IProblem.MissingNullAnnotationType, args, args, 0, 0); +} public void missingOverrideAnnotation(AbstractMethodDeclaration method) { int severity = computeSeverity(IProblem.MissingOverrideAnnotation); if (severity == ProblemSeverities.Ignore) return; @@ -6078,6 +6142,51 @@ 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 possiblyNullToNonNullLocal(char[] variableName, Expression expression, int nullStatus, char[][] annotationName) { + int problemId = IProblem.NonNullLocalInsufficientInfo; + if ((nullStatus & FlowInfo.NULL) != 0) + problemId = IProblem.DefiniteNullToNonNullLocal; + else if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) + problemId = IProblem.PotentialNullToNonNullLocal; + String[] arguments = new String[] { + String.valueOf(variableName), + String.valueOf(annotationName[annotationName.length-1]) + }; + this.handle( + problemId, + arguments, + arguments, + expression.sourceStart, + expression.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 15 Jan 2011 17:20:04 -0000 @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2010 IBM Corporation and others. +# Copyright (c) 2000, 2011 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 @@ -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,22 @@ 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 = 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 = 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 = Null contract violation: assigning null to local variable {0}, which is declared as @{1}. +887 = Null contract violation: potentially assigning null to local variable {0}, which is declared as @{1}. +888 = Potential null contract violation: insufficient nullness information regarding a value that is assigned to local variable {0}, which is declared as @{1}. +889 = Buildpath problem: emulation of type {0} is requested (for null annotations) but a type of this name exists on the build path. +890 = Buildpath problem: the type {0} which is configured as a null annotation type cannot be resolved. +891 = Cannot relax null contract for method return, inherited method from {0} is declared as @{1}. +892 = Cannot tighten null contract for parameter {0}, inherited method from {1} declares this parameter as @{2}. +893 = Cannot tighten null contract for parameter {0}, inherited method from {1} does not constrain this parameter. + ### 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 15 Jan 2011 17:20:15 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -81,8 +81,16 @@ * COMPILER_PB_UNUSED_DECLARED_THROWN_EXCEPTION_EXEMPT_EXCEPTION_AND_THROWABLE * IBM Corporation - added getOptionForConfigurableSeverity(int) * 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 the following constants: + * COMPILER_PB_UNUSED_OBJECT_ALLOCATION + * COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS + * COMPILER_NULLABLE_ANNOTATION_NAME + * COMPILER_NONNULL_ANNOTATION_NAME + * COMPILER_EMULATE_NULL_ANNOTATION_TYPES + * COMPILER_DEFAULT_IMPORT_NULL_ANNOTATION_TYPES + * COMPILER_PB_NULL_CONTRACT_VIOLATION + * COMPILER_PB_POTENTIAL_NULL_CONTRACT_VIOLATION + * COMPILER_PB_NULL_CONTRACT_INSUFFICIENT_INFO *******************************************************************************/ package org.eclipse.jdt.core; @@ -1576,6 +1584,172 @@ */ public static final String COMPILER_PB_POTENTIAL_NULL_REFERENCE = PLUGIN_ID + ".compiler.problem.potentialNullReference"; //$NON-NLS-1$ /** + * Compiler option ID: Name of Annotation Type for Nullable Types. + *

This option defines a fully qualified Java type name that the compiler may use + * to perform special null analysis.

+ *

If the annotation specified by this option is applied to a type in a method + * signature or variable declaration this will be interpreted as a contract that + * null is a legal value in that position. Currently supported + * positions are: method parameters, method return type and local variables.

+ *

If a value whose type + * is annotated with this annotation is dereferenced without checking for null + * the compiler will trigger a diagnostic as further controlled by + * {@link #COMPILER_PB_POTENTIAL_NULL_REFERENCE}.

+ *

The compiler may furthermore check adherence to the null contract as further + * controlled by {@link #COMPILER_PB_NULL_CONTRACT_VIOLATION}, + * {@link #COMPILER_PB_POTENTIAL_NULL_CONTRACT_VIOLATION} and + * {@link #COMPILER_PB_NULL_CONTRACT_INSUFFICIENT_INFO}. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.annotation.nullable"
+ *
Possible values:
any legal Java type name
+ *
Default:
"org.eclipse.jdt.annotation.Nullable"
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_NULLABLE_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nullable"; //$NON-NLS-1$ + /** + * Compiler option ID: Name of Annotation Type for Non-Null Types. + *

This option defines a fully qualified Java type name that the compiler may use + * to perform special null analysis.

+ *

If the annotation specified by this option is applied to a type in a method + * signature or variable declaration this will be interpreted as a contract that + * null is not a legal value in that position. Currently + * supported positions are: method parameters, method return type and local variables.

+ *

For values declared with this annotation the compiler will never trigger a null + * reference diagnostic (as controlled by {@link #COMPILER_PB_POTENTIAL_NULL_REFERENCE} + * and {@link #COMPILER_PB_NULL_REFERENCE}), because the assumption is made that null + * will never occur at runtime in these positions.

+ *

The compiler may furthermore check adherence to the null contract as further + * controlled by {@link #COMPILER_PB_NULL_CONTRACT_VIOLATION}, + * {@link #COMPILER_PB_POTENTIAL_NULL_CONTRACT_VIOLATION} and + * {@link #COMPILER_PB_NULL_CONTRACT_INSUFFICIENT_INFO}. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.annotation.nonnull"
+ *
Possible values:
any legal Java type name
+ *
Default:
"org.eclipse.jdt.annotation.NonNull"
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_NONNULL_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nonnull"; //$NON-NLS-1$ + /** + * Compiler option ID: Emulate Null Annotation Types. + *

When enabled, the compiler will use the annotation types specified in + * {@link #COMPILER_NONNULL_ANNOTATION_NAME} and {@link #COMPILER_NULLABLE_ANNOTATION_NAME} + * without searching for a corresponding type definition, ie., these annotation + * types don't have to actually exist.

+ *

This option is used to make null contract analysis independent of any additional + * classes that would otherwise need to be supplied at compile time.

+ *
Option id:
"org.eclipse.jdt.core.compiler.annotation.emulate"
+ *
Possible values:
{ "disabled", "enabled" }
+ *
Default:
"disabled"
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_EMULATE_NULL_ANNOTATION_TYPES = PLUGIN_ID + ".compiler.annotation.emulate"; //$NON-NLS-1$ + /** + * Compiler option ID: Default Import of Null Annotation Types. + *

When enabled, the compiler will be able to resolve the annotation types specified in + * {@link #COMPILER_NONNULL_ANNOTATION_NAME} and {@link #COMPILER_NULLABLE_ANNOTATION_NAME} + * by their simple names without an explicit import statement.

+ *

This option is used to avoid mentioning the fully qualified annotation names + * in any source files, as to facility the migration from one set of annotations + * to another, e.g., when standard annotations for this purpose will be defined + * in the future. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.annotation.defaultImport"
+ *
Possible values:
{ "disabled", "enabled" }
+ *
Default:
"disabled"
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_DEFAULT_IMPORT_NULL_ANNOTATION_TYPES = PLUGIN_ID + ".compiler.annotation.defaultImport"; //$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: + *

    + *
  1. A method declared with a nonnull annotation returns an expression that is + * statically known to evaluate to a null value.
  2. + *
  3. 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.
  4. + *
  5. A method that overrides or implements an inherited method declared with a nonnull + * annotation tries to relax that contract by specifying a nullable annotation + * (prohibition of contravariant return).
  6. + *
  7. A method that overrides or implements 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).
  8. + *
+ *

+ *

The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and + * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler + * shall interpret as nonnull or nullable annotations, respectively. + *

+ *
+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.nullContractViolation"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"error"
+ *
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_PB_NULL_CONTRACT_VIOLATION = PLUGIN_ID + ".compiler.problem.nullContractViolation"; //$NON-NLS-1$ + /** + * Compiler option ID: Reporting Violations of Null Contracts with Potential Null Value. + *

When enabled, the compiler will issue an error or a warning whenever one of the + * following situations is detected: + *

    + *
  1. A method declared with a nonnull annotation returns an expression that is + * statically known to evaluate to a null value on some flow.
  2. + *
  3. 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.
  4. + *
+ *

+ *

The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and + * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler + * shall interpret as nonnull or nullable annotations, respectively. + *

+ *
+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.potentialNullContractViolation"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"error"
+ *
+ * @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: + *

    + *
  1. 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.
  2. + *
  3. 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.
  4. + *
+ * Insufficient nullness information is usually a consequence of using other unannotated + * variables or methods. + *

+ *

The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and + * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler + * shall interpret as nonnull or nullable annotations, respectively. + *

+ *
+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.nullContractInsufficientInfo"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"warning"
+ *
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_PB_NULL_CONTRACT_INSUFFICIENT_INFO = PLUGIN_ID + ".compiler.problem.nullContractInsufficientInfo"; //$NON-NLS-1$ + /** * Compiler option ID: Reporting Redundant Null Check. *

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 15 Jan 2011 17:20:43 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -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" + + "