blob: 11400919dfb7d2553de503d0026b15930fa782fd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 GK Software AG 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.objectteams.internal.jdt.nullity;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.jdt.internal.compiler.impl.IrritantSet;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.HashtableOfInt;
import org.objectteams.Instantiation;
import org.objectteams.InstantiationPolicy;
import static org.eclipse.objectteams.internal.jdt.nullity.Constants.IProblem;
import static org.eclipse.objectteams.internal.jdt.nullity.Constants.TagBits;
import static org.eclipse.objectteams.internal.jdt.nullity.Constants.TypeIds;
import base org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import base org.eclipse.jdt.internal.compiler.ast.Assignment;
import base org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import base org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import base org.eclipse.jdt.internal.compiler.ast.MessageSend;
import base org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import base org.eclipse.jdt.internal.compiler.ast.Statement;
import base org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import base org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import base org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import base org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import base org.eclipse.jdt.internal.compiler.lookup.MethodVerifier15;
import base org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import base org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import base org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import base org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import base org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import base org.eclipse.jdt.internal.core.JavaProject;
import base org.eclipse.jdt.internal.core.JavaModelManager;
/**
* This team class adds the implementation from
* https://bugs.eclipse.org/bugs/186342 - [compiler][null] Using annotations for null checking
* to the compiler.
*
* It essentially reflects the state of https://bugs.eclipse.org/bugs/attachment.cgi?id=186890
*/
@SuppressWarnings("restriction")
public team class CompilerAdaptation {
public CompilerAdaptation () {
// add more irritants to IrritantSet:
IrritantSet.COMPILER_DEFAULT_ERRORS.set( NullCompilerOptions.NullContractViolation
|NullCompilerOptions.PotentialNullContractViolation);
IrritantSet.COMPILER_DEFAULT_WARNINGS.set(NullCompilerOptions.NullContractInsufficientInfo);
IrritantSet.NULL.set( NullCompilerOptions.NullContractViolation
|NullCompilerOptions.PotentialNullContractViolation
|NullCompilerOptions.NullContractInsufficientInfo);
}
// ======================= Statement level analysis ============================
@SuppressWarnings({"abstractrelevantrole", "hidden-lifting-problem"}) // due to abstractness of this role failed lifting could theoretically block callin triggers
@Instantiation(InstantiationPolicy.ALWAYS)
protected abstract class Statement playedBy Statement {
abstract Expression getExpression();
// use custom hook from JDT/Core (https://bugs.eclipse.org/335093)
checkAgainstNullAnnotation <- replace checkAgainstNullAnnotation;
/** Check assignment to local with null annotation. */
@SuppressWarnings("basecall")
callin int checkAgainstNullAnnotation(BlockScope currentScope, LocalVariableBinding local,int nullStatus)
{
if ( local != null
&& (local.tagBits & TagBits.AnnotationNonNull) != 0
&& nullStatus != FlowInfo.NON_NULL)
{
currentScope.problemReporter().possiblyNullToNonNullLocal(local.name, getExpression(), nullStatus,
currentScope.environment().getNonNullAnnotationName());
nullStatus=FlowInfo.NON_NULL;
}
return nullStatus;
}
}
@Instantiation(InstantiationPolicy.ALWAYS)
protected class Assignment extends Statement playedBy Assignment {
/** Wire required method of super class. */
Expression getExpression() -> get Expression expression;
}
@Instantiation(InstantiationPolicy.ALWAYS)
protected class LocalDeclaration extends Statement playedBy LocalDeclaration {
/** Wire required method of super class. */
Expression getExpression() -> get Expression initialization;
}
/** Analyse argument expressions as part of a MessageSend, check against method parameter annotation. */
@Instantiation(InstantiationPolicy.ALWAYS)
protected class MessageSend playedBy MessageSend {
Expression[] getArguments() -> get Expression[] arguments;
MethodBinding getBinding() -> get MethodBinding binding;
nullStatus <- replace nullStatus;
@SuppressWarnings("basecall")
callin int nullStatus(FlowInfo flowInfo) {
int status = getNullStatus();
if (status != FlowInfo.UNKNOWN)
return status;
return base.nullStatus(flowInfo);
}
void analyseArguments(BlockScope currentScope, FlowInfo flowInfo)
<- before FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)
with { currentScope <- currentScope, flowInfo <- flowInfo }
void analyseArguments(BlockScope currentScope, FlowInfo flowInfo) {
// compare actual null-status against parameter annotations of the called method:
MethodBinding methodBinding = getBinding();
Expression[] arguments = getArguments();
if (arguments != null && methodBinding.parameterNonNullness != null) {
int length = arguments.length;
for (int i = 0; i < length; i++) {
int nullStatus = arguments[i].nullStatus(flowInfo); // slight loss of precision: should also use the null info from the receiver.
if ( nullStatus != FlowInfo.NON_NULL
&& methodBinding.parameterNonNullness[i] != null)
{
if (methodBinding.parameterNonNullness[i].booleanValue()) // if @NonNull is required
{
char[][] annotationName = currentScope.environment().getNonNullAnnotationName();
currentScope.problemReporter().possiblyNullToNonNullParameter(arguments[i], nullStatus, annotationName[annotationName.length-1]);
}
}
}
}
}
checkNPE <- after checkNPE;
/** Detect and signal directly dereferencing a nullable message send result. */
void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
if (getNullStatus() == FlowInfo.POTENTIALLY_NULL)
scope.problemReporter().messageSendPotentialNullReference(getBinding(), this);
}
protected int getNullStatus() {
MethodBinding binding = this.getBinding();
if (binding.isValidBinding()) {
// try to retrieve null status of this message send from an annotation of the called method:
long tagBits = binding.getTagBits();
if ((tagBits & TagBits.AnnotationNonNull) != 0)
return FlowInfo.NON_NULL;
if ((tagBits & TagBits.AnnotationNullable) != 0)
return FlowInfo.POTENTIALLY_NULL;
}
return FlowInfo.UNKNOWN;
}
}
@Instantiation(InstantiationPolicy.ALWAYS)
protected class EqualExpression playedBy EqualExpression {
MessageSend getLeftMessage() -> get Expression left
with { result <- (left instanceof MessageSend) ? (MessageSend) left : null }
int getLeftNullStatus(FlowInfo info) -> get Expression left
with { result <- left.nullStatus(info) }
MessageSend getRightMessage() -> get Expression right
with { result <- (right instanceof MessageSend) ? (MessageSend) right : null }
int getRightNullStatus(FlowInfo info)-> get Expression right
with { result <- right.nullStatus(info) }
checkNullComparison <- before checkNullComparison;
/** Detect and signal when comparing a non-null message send against null. */
void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo,
FlowInfo initsWhenTrue, FlowInfo initsWhenFalse)
{
MessageSend leftMessage = getLeftMessage();
if ( leftMessage != null
&& leftMessage.getNullStatus() == FlowInfo.NON_NULL
&& getRightNullStatus(flowInfo) == FlowInfo.NULL)
{
scope.problemReporter().messageSendRedundantCheckOnNonNull(leftMessage.getBinding(), leftMessage);
}
MessageSend rightMessage = getRightMessage();
if ( rightMessage != null
&& rightMessage.getNullStatus() == FlowInfo.NON_NULL
&& getLeftNullStatus(flowInfo) == FlowInfo.NULL)
{
scope.problemReporter().messageSendRedundantCheckOnNonNull(rightMessage.getBinding(), rightMessage);
}
}
}
/** Analyse the expression within a return statement, check against method return annotation. */
@Instantiation(InstantiationPolicy.ALWAYS)
protected class ReturnStatement playedBy ReturnStatement {
Expression getExpression() -> get Expression expression;
int getSourceStart() -> get int sourceStart;
int getSourceEnd() -> get int sourceEnd;
void analyseNull(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)
<- after FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo);
void analyseNull(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
Expression expression = getExpression();
if (expression != null) {
flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo); // may cause some issues to be reported twice :(
int nullStatus = expression.nullStatus(flowInfo);
if (nullStatus != FlowInfo.NON_NULL) {
// if we can't prove non-null check against declared null-ness of the enclosing method:
long tagBits;
try {
tagBits = currentScope.methodScope().referenceMethod().binding.tagBits;
} catch (NullPointerException npe) {
return;
}
if ((tagBits & TagBits.AnnotationNonNull) != 0) {
char[][] annotationName = currentScope.environment().getNonNullAnnotationName();
currentScope.problemReporter().possiblyNullFromNonNullMethod(this, nullStatus,
annotationName[annotationName.length-1]);
}
}
}
}
}
// ======================= Method level annotations ============================
@SuppressWarnings("bindingconventions")
@Instantiation(InstantiationPolicy.ALWAYS)
protected class StandardAnnotation playedBy Annotation {
@SuppressWarnings("decapsulation")
detectStandardAnnotation <- replace detectStandardAnnotation;
callin long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute)
{
long tagBits = base.detectStandardAnnotation(scope, annotationType, valueAttribute);
switch (annotationType.id) {
case TypeIds.T_ConfiguredAnnotationNullable :
tagBits |= TagBits.AnnotationNullable;
break;
case TypeIds.T_ConfiguredAnnotationNonNull :
tagBits |= TagBits.AnnotationNonNull;
break;
case TypeIds.T_ConfiguredAnnotationNullableByDefault :
tagBits |= TagBits.AnnotationNullableByDefault;
break;
case TypeIds.T_ConfiguredAnnotationNonNullByDefault :
tagBits |= TagBits.AnnotationNonNullByDefault;
break;
}
return tagBits;
}
}
protected class AbstractMethodDeclaration playedBy AbstractMethodDeclaration {
int sourceEnd() -> int sourceEnd();
int sourceStart() -> int sourceStart();
BlockScope getScope() -> get MethodScope scope;
Argument[] getArguments() -> get Argument[] arguments;
Annotation[] getAnnotations() -> get Annotation[] annotations;
void setAnnotations(Annotation[] annot) -> set Annotation[] annotations;
MethodBinding getBinding() -> get MethodBinding binding;
void bindArguments() -> void bindArguments();
void resolveArgumentNullAnnotations() <- replace void bindArguments();
@SuppressWarnings("basecall")
callin void resolveArgumentNullAnnotations() {
MethodBinding binding = getBinding();
if (binding != null) {
if ((binding.getTagBits() & TagBits.HasBoundArguments) != 0) // avoid double execution
return;
binding.addTagBit(TagBits.HasBoundArguments);
}
base.resolveArgumentNullAnnotations();
Argument[] arguments = this.getArguments();
if (arguments != null && binding != null) {
for (int i = 0, length = arguments.length; i < length; i++) {
Argument argument = arguments[i];
// transfer nullness info from the argument to the method:
if ((argument.binding.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0) {
if (binding.parameterNonNullness == null)
binding.parameterNonNullness = new Boolean[arguments.length];
binding.parameterNonNullness[i] = Boolean.valueOf((argument.binding.tagBits & TagBits.AnnotationNonNull) != 0);
}
}
}
}
/** Feed null status from parameter annotation into the analysis of the method's body. */
void analyseArgumentNullity(FlowInfo info)
<- before void analyseCode(ClassScope classScope, InitializationFlowContext initializationContext, FlowInfo info)
with { info <- info }
private void analyseArgumentNullity(FlowInfo info) {
MethodBinding binding = getBinding();
Argument[] arguments = this.getArguments();
if (arguments != null && binding.parameterNonNullness != null) {
for (int i = 0, count = arguments.length; i < count; i++) {
// leverage null-info from parameter annotations:
Boolean nonNullNess = binding.parameterNonNullness[i];
if (nonNullNess != null) {
// FIXME(SH): workaround for missing array growing in markAsDefinitelyNonNull/markPotentiallyNullBit:
// see also Bug 247564 - [compiler][null] Detecting null field reference (our issues is fixed in proposed attachments)
int id = arguments[i].binding.id;
if (info instanceof UnconditionalFlowInfo)
id += ((UnconditionalFlowInfo)info).maxFieldCount;
if (id >= UnconditionalFlowInfo.BitCacheSize)
info.markAsDefinitelyAssigned(arguments[i].binding);
// End workaround
if (nonNullNess)
info.markAsDefinitelyNonNull(arguments[i].binding);
else
info.markPotentiallyNullBit(arguments[i].binding);
}
}
}
}
/**
* Materialize a null annotation that has been added from the current default,
* in order to ensure that this annotation will be generated into the .class file, too.
*/
public void addNullnessAnnotation(long defaultNullness, ReferenceBinding annotationBinding) {
Annotation[] annotations = getAnnotations();
setAnnotations(addAnnotation(annotations, annotationBinding));
}
/**
* Metarialize a null parameter annotation that has been added from the current default,
* in order to ensure that this annotation will be generated into the .class file, too.
*/
public void addParameterNullnessAnnotation(int i, long defaultNullness, ReferenceBinding annotationBinding) {
Argument argument = getArguments()[i];
Annotation[] annotations = argument.annotations;
argument.annotations = addAnnotation(annotations, annotationBinding);
}
Annotation[] addAnnotation(Annotation[] annotations, ReferenceBinding annotationBinding) {
int sourceStart = this.sourceStart();
long pos = ((long)sourceStart<<32) + this.sourceEnd();
long[] poss = new long[annotationBinding.compoundName.length];
Arrays.fill(poss, pos);
MarkerAnnotation annotation = new MarkerAnnotation(new QualifiedTypeReference(annotationBinding.compoundName, poss), sourceStart);
annotation.resolvedType = annotationBinding;
if (annotations == null) {
annotations = new Annotation[] {annotation};
} else {
int len = annotations.length;
System.arraycopy(annotations, 0, annotations=new Annotation[len+1], 1, len);
annotations[0] = annotation;
}
return annotations;
}
}
/** Add a field to store parameter nullness information. */
protected class MethodBinding playedBy MethodBinding {
/** Store nullness information from annotation (incl. inherited contracts). */
public Boolean[] parameterNonNullness; // TRUE means @NonNull declared, FALSE means @Nullable declared, null means nothing declared
ReferenceBinding getDeclaringClass() -> get ReferenceBinding declaringClass;
TypeBinding[] getParameters() -> get TypeBinding[] parameters;
long getTagBits() -> get long tagBits;
public void addTagBit(long bit) -> set long tagBits
with { bit | getTagBits() -> tagBits }
boolean isStatic() -> boolean isStatic();
boolean isValidBinding() -> boolean isValidBinding();
AbstractMethodDeclaration sourceMethod()-> AbstractMethodDeclaration sourceMethod();
char[] readableName() -> char[] readableName();
/** After method verifier has finished, fill in missing nullness values from the applicable default. */
protected void fillInDefaultNullness(long defaultNullness, TypeBinding annotationBinding) {
if (this.parameterNonNullness == null)
this.parameterNonNullness = new Boolean[getParameters().length];
Boolean value = Boolean.valueOf(defaultNullness == TagBits.AnnotationNonNull);
AbstractMethodDeclaration sourceMethod = sourceMethod();
for (int i = 0; i < this.parameterNonNullness.length; i++) {
boolean added = false;
if (this.parameterNonNullness[i] == null) {
added = true;
this.parameterNonNullness[i] = value;
if (sourceMethod != null)
sourceMethod.addParameterNullnessAnnotation(i, defaultNullness, (ReferenceBinding)annotationBinding);
}
if (added)
addTagBit(TagBits.HasParameterAnnotations);
}
if ((getTagBits() & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0) {
addTagBit(defaultNullness);
if (sourceMethod != null)
sourceMethod.addNullnessAnnotation(defaultNullness, (ReferenceBinding)annotationBinding);
}
}
}
/** Transfer inherited null contracts and check compatibility. */
@SuppressWarnings("decapsulation")
protected class MethodVerifier15 playedBy MethodVerifier15 {
LookupEnvironment getEnvironment() -> get LookupEnvironment environment;
SourceTypeBinding getType() -> get SourceTypeBinding type;
AbstractMethodDeclaration[] getMethodDeclarations() -> get SourceTypeBinding type
with { result <- type.scope.referenceContext.methods }
MethodBinding[] getMethodBindings() -> get SourceTypeBinding type
with { result <- type.methods() }
void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding[] methods, int length)
<- after
void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods);
void bindMethodArguments() <- after void computeMethods();
void fillInDefaultNullness() <- after void checkMethods();
void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding[] methods, int length) {
// TODO: change traversal: process all methods at once!
for (int i = length; --i >= 0;)
if (!currentMethod.isStatic() && !methods[i].isStatic())
checkNullContractInheritance(currentMethod, methods[i]);
}
void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding inheritedMethod) {
long inheritedBits = inheritedMethod.getTagBits();
long currentBits = currentMethod.getTagBits();
LookupEnvironment environment = this.getEnvironment();
if ((inheritedBits & TagBits.HasBoundArguments) == 0) {
ReferenceBinding supertype = inheritedMethod.getDeclaringClass();
if (!((ReferenceBinding) supertype.erasure()).isBinaryBinding()) {
AbstractMethodDeclaration sourceMethod = inheritedMethod.sourceMethod();
if (sourceMethod != null)
sourceMethod.bindArguments();
}
}
// return type:
if ((inheritedBits & TagBits.AnnotationNonNull) != 0) {
if ((currentBits & TagBits.AnnotationNullable) != 0) {
AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod();
getType().problemReporter().illegalRedefinitionToNullableReturn(methodDecl, inheritedMethod.getDeclaringClass(),
environment.getNonNullAnnotationName());
}
}
if ((currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0)
currentMethod.addTagBit(inheritedBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable));
// parameters:
Argument[] currentArguments = currentMethod.sourceMethod().getArguments();
if (inheritedMethod.parameterNonNullness != null) {
// inherited method has null-annotations, check and possibly transfer:
// prepare for transferring (contract inheritance):
if (currentMethod.parameterNonNullness == null)
currentMethod.parameterNonNullness = new Boolean[currentMethod.getParameters().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
getType().problemReporter().illegalRedefinitionToNonNullParameter(
currentArguments[i],
inheritedMethod.getDeclaringClass(),
inheritedNonNullNess == null
? null
: environment.getNullableAnnotationName());
continue;
}
}
if (currentMethod.parameterNonNullness[i] == null && inheritedNonNullNess != null) {
// inherit this annotation as the current method has no annotation:
currentMethod.parameterNonNullness[i] = inheritedNonNullNess;
VariableBinding argumentBinding = currentArguments[i].binding;
argumentBinding.tagBits |= inheritedNonNullNess.booleanValue()
? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
}
}
} else if (currentMethod.parameterNonNullness != null) {
// Temporary tweak:
// if (!((ReferenceBinding) inheritedMethod.getDeclaringClass().erasure()).isBinaryBinding()) // WORKAROUND WHILE PLAYING WITH DEFAULTS
// 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
getType().problemReporter().illegalRedefinitionToNonNullParameter(
currentArguments[i],
inheritedMethod.getDeclaringClass(),
null);
}
}
}
}
void bindMethodArguments() {
// binding the arguments is required for null contract checking, which needs argument annotations
AbstractMethodDeclaration[] methodDeclarations = getMethodDeclarations();
if (methodDeclarations != null)
for (AbstractMethodDeclaration methodDecl : methodDeclarations)
methodDecl.bindArguments();
}
/**
* after checkMethods has passed down inherited nullness info,
* now fill in missing annotations from a default setting from an enclosing scope.
*/
void fillInDefaultNullness() {
TypeBinding annotationBinding = findDefaultNullness();
// apply this default to all methods:
if (annotationBinding != null) {
long defaultNullness = Constants.getNullnessTagbit(annotationBinding);
MethodBinding[] methodBindings = getMethodBindings();
if (methodBindings != null) {
for (MethodBinding methodBinding : methodBindings)
methodBinding.fillInDefaultNullness(defaultNullness, annotationBinding);
}
}
}
TypeBinding findDefaultNullness() {
// find the applicable default inside->out:
// type
SourceTypeBinding type = getType();
SourceTypeBinding currentType = type;
TypeBinding annotationBinding = null;
while (currentType != null) {
annotationBinding = currentType.nullnessDefaultAnnotation;
if (annotationBinding != null)
return annotationBinding;
currentType = currentType.enclosingType();
}
// package
annotationBinding = type.getPackage().nullnessDefaultAnnotation;
if (annotationBinding != null)
return annotationBinding;
// global
long defaultNullness = getEnvironment().getGlobalOptions().defaultNonNullness;
if (defaultNullness != 0) {
annotationBinding = getEnvironment().getNullAnnotationBinding(defaultNullness);
if (annotationBinding != null)
return annotationBinding;
// on this branch default was not defined using an annotation, thus annotation type can still be missing
if (defaultNullness == TagBits.AnnotationNonNull)
type.problemReporter().missingNullAnnotationType(getEnvironment().getNonNullAnnotationName());
else if (defaultNullness == TagBits.AnnotationNullable)
type.problemReporter().missingNullAnnotationType(getEnvironment().getNullableAnnotationName());
else
type.problemReporter().abortDueToInternalError("Illegal default nullness value: "+defaultNullness); //$NON-NLS-1$
// reset default to avoid duplicate errors:
getEnvironment().getGlobalOptions().defaultNonNullness = 0;
}
return null;
}
}
@SuppressWarnings("bindingconventions")
protected class TaggableTypeBinding playedBy TypeBinding {
long getTagBits() -> get long tagBits;
public void addTagBit(long bit) -> set long tagBits
with { bit | getTagBits() -> tagBits }
}
protected class SourceTypeBinding extends TaggableTypeBinding playedBy SourceTypeBinding {
PackageBinding getPackage() -> PackageBinding getPackage();
ProblemReporter problemReporter() -> get ClassScope scope
with { result <- scope.problemReporter() }
SourceTypeBinding enclosingType() -> ReferenceBinding enclosingType()
with { result <- (SourceTypeBinding)result }
protected TypeBinding nullnessDefaultAnnotation;
/** initialize a normal type */
evaluateNullAnnotations <- after getAnnotationTagBits;
/** initialize a package-info.java */
evaluateNullAnnotations <- after initializeDeprecatedAnnotationTagBits
base when (CharOperation.equals(base.sourceName, TypeConstants.PACKAGE_INFO_NAME));
@SuppressWarnings("inferredcallout")
void evaluateNullAnnotations() {
// transfer nullness info from tagBits to this.nullnessDefaultAnnotation
long tagBit = Constants.applyDefaultNullnessTagbit(getTagBits());
if (tagBit == 0)
return;
TypeBinding nullnessDefaultAnnotation = getPackage().getEnvironment().getNullAnnotationBinding(tagBit);
if (nullnessDefaultAnnotation != null) {
if (CharOperation.equals(this.sourceName, TypeConstants.PACKAGE_INFO_NAME)) {
getPackage().nullnessDefaultAnnotation = nullnessDefaultAnnotation;
} else {
this.nullnessDefaultAnnotation = nullnessDefaultAnnotation;
}
}
}
}
/** Retrieve null annotations from binary methods. */
protected class BinaryTypeBinding extends TaggableTypeBinding playedBy BinaryTypeBinding {
@SuppressWarnings("decapsulation")
LookupEnvironment getEnvironment() -> get LookupEnvironment environment;
PackageBinding getPackage() -> PackageBinding getPackage();
char[] sourceName() -> char[] sourceName();
/** Constructor for emulated annotation type. */
@SuppressWarnings({ "inferredcallout", "decapsulation" })
protected BinaryTypeBinding(char[][] compoundName, ReferenceBinding superclass, org.eclipse.jdt.internal.compiler.lookup.PackageBinding packageBinding, int typeId)
{
base();
this.compoundName = compoundName;
this.sourceName = compoundName[compoundName.length-1];
this.modifiers = ClassFileConstants.AccAnnotation | ClassFileConstants.AccPublic;
this.fields = Binding.NO_FIELDS;
this.methods = Binding.NO_METHODS;
this.memberTypes = Binding.NO_MEMBER_TYPES;
this.superclass = superclass;
this.superInterfaces = Binding.NO_SUPERINTERFACES;
this.fPackage = packageBinding;
this.typeVariables = Binding.NO_TYPE_VARIABLES;
long applicableFor = 0;
if (typeId == TypeIds.T_ConfiguredAnnotationNullable || typeId == TypeIds.T_ConfiguredAnnotationNonNull)
applicableFor = TagBits.AnnotationForMethod | TagBits.AnnotationForParameter | TagBits.AnnotationForLocalVariable;
else if (typeId == TypeIds.T_ConfiguredAnnotationNullable || typeId == TypeIds.T_ConfiguredAnnotationNonNull)
applicableFor = TagBits.AnnotationForPackage | TagBits.AnnotationForType;
this.tagBits = TagBits.AreFieldsComplete | TagBits.AreFieldsSorted
| TagBits.AreMethodsComplete | TagBits.AreMethodsSorted
| TagBits.HasNoMemberTypes | TagBits.TypeVariablesAreConnected
| TagBits.AnnotationClassRetention
| applicableFor;
this.id = typeId;
// experiment with providing a file name that the Java model can recognize as emulated:
// this.fileName = CharOperation.concat(new char[]{'&'}, CharOperation.concatWith(compoundName, '/'));
}
void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBinding)
<- after MethodBinding createMethod(IBinaryMethod method, long sourceLevel, char[][][] missingTypeNames)
with { method <- method, methodBinding <- result }
void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBinding) {
LookupEnvironment environment = this.getEnvironment();
char[][] nullableAnnotationName = environment.getNullableAnnotationName();
char[][] nonNullAnnotationName = environment.getNonNullAnnotationName();
if (nullableAnnotationName == null || nonNullAnnotationName == null)
return; // not configured to use null annotations
// return:
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)) {
methodBinding.addTagBit(TagBits.AnnotationNonNull);
break;
}
if (CharOperation.equals(typeName, nullableAnnotationName)) {
methodBinding.addTagBit(TagBits.AnnotationNullable);
break;
}
}
}
// parameters:
TypeBinding[] parameters = methodBinding.getParameters();
for (int j = 0; j < 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 (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[parameters.length];
methodBinding.parameterNonNullness[j] = Boolean.TRUE;
break;
} else if (CharOperation.equals(typeName, nullableAnnotationName)) {
if (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[parameters.length];
methodBinding.parameterNonNullness[j] = Boolean.FALSE;
break;
}
}
}
}
}
scanTypeForNullAnnotation <- after cachePartsFrom;
void scanTypeForNullAnnotation(IBinaryType binaryType) {
LookupEnvironment environment = this.getEnvironment();
char[][] nullableByDefaultAnnotationName = environment.getNullableByDefaultAnnotationName();
char[][] nonNullByDefaultAnnotationName = environment.getNonNullByDefaultAnnotationName();
if (nullableByDefaultAnnotationName == null || nonNullByDefaultAnnotationName == null)
return; // not configured to use null annotations
IBinaryAnnotation[] annotations = binaryType.getAnnotations();
if (annotations != null) {
long annotationBit = 0L;
TypeBinding defaultNullness = 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, nonNullByDefaultAnnotationName)) {
annotationBit = TagBits.AnnotationNonNullByDefault;
defaultNullness = getEnvironment().getNullAnnotationBinding(TagBits.AnnotationNonNull);
break;
}
if (CharOperation.equals(typeName, nullableByDefaultAnnotationName)) {
annotationBit = TagBits.AnnotationNullableByDefault;
defaultNullness = getEnvironment().getNullAnnotationBinding(TagBits.AnnotationNullable);
break;
}
}
if (annotationBit != 0L) {
addTagBit(annotationBit);
if (CharOperation.equals(this.sourceName(), TypeConstants.PACKAGE_INFO_NAME))
this.getPackage().nullnessDefaultAnnotation = defaultNullness;
}
}
}
}
// ========================== Configuration of null annotation types =========================
/** Initiate setup of configured null annotation types. */
protected class LookupEnvironment playedBy LookupEnvironment {
ProblemReporter getProblemReporter() -> get ProblemReporter problemReporter;
ReferenceBinding getType(char[][] compoundName) -> ReferenceBinding getType(char[][] compoundName);
PackageBinding createPackage(char[][] compoundName) -> PackageBinding createPackage(char[][] compoundName);
boolean packageInitialized = false;
/** The first time a package is requested initialize the null annotation type package. */
void initNullAnnotationPackage()
<- before PackageBinding getTopLevelPackage(char[] name),
PackageBinding createPackage(char[][] compoundName)
when (!this.packageInitialized);
private void initNullAnnotationPackage() {
this.packageInitialized = true;
char[][] compoundName = getNullableAnnotationName();
if (compoundName != null)
setupNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNullable);
compoundName = getNonNullAnnotationName();
if (compoundName != null)
setupNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNonNull);
compoundName = getNullableByDefaultAnnotationName();
if (compoundName != null)
setupNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNullableByDefault);
compoundName = getNonNullByDefaultAnnotationName();
if (compoundName != null)
setupNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNonNullByDefault);
}
/**
* Create or retrieve the package holding the specified type.
* If emulation is enabled fill it with an emulated type of the given name.
* Prepare the package with all information to do the second stage of initialization/checking.
*/
void setupNullAnnotationPackage(char[][] typeName, int typeId) {
char[][] packageName = CharOperation.subarray(typeName, 0, typeName.length-1);
PackageBinding packageBinding = createPackage(packageName);
char[] simpleTypeName = typeName[typeName.length-1];
if (getGlobalOptions().emulateNullAnnotationTypes) {
// before adding an emulated type, check if type this type already exists:
ReferenceBinding existingType = packageBinding.getType(simpleTypeName);
if (existingType != null && existingType.isValidBinding())
getProblemReporter().conflictingTypeEmulation(typeName);
else
packageBinding.addType(new BinaryTypeBinding(
typeName,
getType(TypeConstants.JAVA_LANG_OBJECT),
packageBinding,
typeId));
packageBinding.shouldEmulate = true;
}
if (typeId == TypeIds.T_ConfiguredAnnotationNullable)
packageBinding.nullableName = simpleTypeName;
else if (typeId == TypeIds.T_ConfiguredAnnotationNonNull)
packageBinding.nonNullName = simpleTypeName;
else if (typeId == TypeIds.T_ConfiguredAnnotationNullableByDefault)
packageBinding.nullableByDefaultName = simpleTypeName;
else if (typeId == TypeIds.T_ConfiguredAnnotationNonNullByDefault)
packageBinding.nonNullByDefaultName = simpleTypeName;
}
CompilerOptions getGlobalOptions() -> get CompilerOptions globalOptions;
public char[][] getNullableAnnotationName() {
return getGlobalOptions().nullableAnnotationName;
}
public char[][] getNonNullAnnotationName() {
return getGlobalOptions().nonNullAnnotationName;
}
public char[][] getNullableByDefaultAnnotationName() {
return getGlobalOptions().nullableByDefaultAnnotationName;
}
public char[][] getNonNullByDefaultAnnotationName() {
return getGlobalOptions().nonNullByDefaultAnnotationName;
}
public TypeBinding getNullAnnotationBinding(long annotationTagBit) {
if (annotationTagBit == TagBits.AnnotationNonNull)
return getType(getNonNullAnnotationName());
if (annotationTagBit == TagBits.AnnotationNullable)
return getType(getNullableAnnotationName());
return null;
}
}
/** The package holding the configured null annotation types detects and marks these annotation types. */
@SuppressWarnings("decapsulation")
protected class PackageBinding playedBy PackageBinding {
LookupEnvironment getEnvironment() -> get LookupEnvironment environment;
protected ReferenceBinding getType(char[] name) -> ReferenceBinding getType(char[] name);
protected void addType(ReferenceBinding element)-> void addType(ReferenceBinding element);
protected char[] nullableName = null;
protected char[] nonNullName = null;
protected char[] nullableByDefaultName = null;
protected char[] nonNullByDefaultName = null;
protected boolean shouldEmulate = false;
protected TypeBinding nullnessDefaultAnnotation;
void setupNullAnnotationType(ReferenceBinding type) <- after void addType(ReferenceBinding type)
when (this.nullableName != null || this.nonNullName != null || this.nullableByDefaultName != null || this.nonNullByDefaultName != null);
void setupNullAnnotationType(ReferenceBinding type) {
int id = 0;
if (CharOperation.equals(this.nullableName, type.sourceName))
id = TypeIds.T_ConfiguredAnnotationNullable;
else if (CharOperation.equals(this.nonNullName, type.sourceName))
id = TypeIds.T_ConfiguredAnnotationNonNull;
else if (CharOperation.equals(this.nullableByDefaultName, type.sourceName))
id = TypeIds.T_ConfiguredAnnotationNullableByDefault;
else if (CharOperation.equals(this.nonNullByDefaultName, type.sourceName))
id = TypeIds.T_ConfiguredAnnotationNonNullByDefault;
else
return;
// we're adding a null annotation type to this package:
if (this.shouldEmulate) // unfortunately here we don't have any source to report the error against.
getEnvironment().getProblemReporter().conflictingTypeEmulation(type.compoundName);
else
type.id = id; // ensure annotations of this type are detected as standard annotations.
}
}
/** Intermediate role to provide access to environment and problemReporter as roles, too. */
protected class BlockScope playedBy BlockScope {
ProblemReporter problemReporter() -> ProblemReporter problemReporter();
MethodScope methodScope() -> MethodScope methodScope();
LookupEnvironment environment() -> LookupEnvironment environment();
}
// ======================= Problem reporting ==================================
/**
* Adapt the base class for handling new problem ids and irritants.
* Add new problem reporting methods.
*/
protected class ProblemReporter playedBy ProblemReporter {
@SuppressWarnings("decapsulation")
void handle(int problemId, String[] problemArguments, String[] messageArguments, int severity,
int problemStartPosition, int problemEndPosition)
->
void handle(int problemId, String[] problemArguments, String[] messageArguments, int severity,
int problemStartPosition, int problemEndPosition);
@SuppressWarnings("decapsulation")
void handle(int problemId, String[] problemArguments, String[] messageArguments, int problemStartPosition, int problemEndPosition)
->
void handle(int problemId, String[] problemArguments, String[] messageArguments, int problemStartPosition, int problemEndPosition);
void abortDueToInternalError(String errorMessage) -> void abortDueToInternalError(String errorMessage);
getProblemCategory <- replace getProblemCategory;
getNullIrritant <- replace getIrritant;
@SuppressWarnings("basecall")
static callin int getProblemCategory(int severity, int problemID) {
categorizeOnIrritant: {
// fatal problems even if optional are all falling into same category (not irritant based)
if ((severity & ProblemSeverities.Fatal) != 0)
break categorizeOnIrritant;
int irritant = getIrritant(problemID);
switch (irritant) {
case CompilerOptions.NullContractViolation :
case CompilerOptions.PotentialNullContractViolation :
case CompilerOptions.NullContractInsufficientInfo :
return CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM;
}
// categorize fatal problems per ID
switch (problemID) {
case IProblem.ConflictingTypeEmulation :
case IProblem.MissingNullAnnotationType :
return CategorizedProblem.CAT_BUILDPATH;
}
}
return base.getProblemCategory(severity, problemID);
}
@SuppressWarnings("basecall")
static callin int getNullIrritant(int problemID) {
int irritant = getIrritant(problemID);
if (irritant != 0)
return irritant;
return base.getNullIrritant(problemID);
}
private static int getIrritant(int problemID) {
switch(problemID) {
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.PotentialNullMessageSendReference:
return org.eclipse.jdt.internal.compiler.impl.CompilerOptions.PotentialNullReference;
case IProblem.RedundantNullCheckOnNonNullMessageSend:
return org.eclipse.jdt.internal.compiler.impl.CompilerOptions.RedundantNullCheck;
}
return 0;
}
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.getSourceStart(),
returnStatement.getSourceEnd());
}
public void possiblyNullToNonNullLocal(char[] variableName, org.eclipse.jdt.internal.compiler.ast.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(org.eclipse.jdt.internal.compiler.ast.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 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(org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration abstractMethodDecl,
ReferenceBinding declaringClass, char[][] nonNullAnnotationName)
{
MethodDeclaration methodDecl = (MethodDeclaration) abstractMethodDecl;
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.returnType.sourceStart,
methodDecl.returnType.sourceEnd);
}
public void messageSendPotentialNullReference(MethodBinding method, ASTNode location) {
String[] arguments = new String[] {new String(method.readableName())};
this.handle(
IProblem.PotentialNullMessageSendReference,
arguments,
arguments,
location.sourceStart,
location.sourceEnd);
}
public void messageSendRedundantCheckOnNonNull(MethodBinding method, ASTNode location) {
String[] arguments = new String[] {new String(method.readableName()) };
this.handle(
IProblem.RedundantNullCheckOnNonNullMessageSend,
arguments,
arguments,
location.sourceStart,
location.sourceEnd);
}
public void missingNullAnnotationType(char[][] nullAnnotationName) {
String[] args = { new String(CharOperation.concatWith(nullAnnotationName, '.')) };
this.handle(IProblem.MissingNullAnnotationType, args, args, 0, 0);
}
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);
}
// NOTE: adaptation of toString() omitted
}
/** Supply more error messages. */
protected class ProblemFactory playedBy DefaultProblemFactory {
@SuppressWarnings("decapsulation")
int keyFromID(int id) -> int keyFromID(int id);
void loadMessageTemplates(HashtableOfInt templates, Locale loc)
<- after HashtableOfInt loadMessageTemplates(Locale loc)
with { templates <- result, loc <- loc }
@SuppressWarnings("rawtypes") // copied from its base class, only bundleName has been changed
public static void loadMessageTemplates(HashtableOfInt templates, Locale loc) {
ResourceBundle bundle = null;
String bundleName = "org.eclipse.objectteams.internal.jdt.nullity.problem_messages"; //$NON-NLS-1$
try {
bundle = ResourceBundle.getBundle(bundleName, loc);
} catch(MissingResourceException e) {
System.out.println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + loc); //$NON-NLS-1$//$NON-NLS-2$
throw e;
}
Enumeration keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
try {
int messageID = Integer.parseInt(key);
templates.put(keyFromID(messageID), bundle.getString(key));
} catch(NumberFormatException e) {
// key ill-formed
} catch (MissingResourceException e) {
// available ID
}
}
}
}
// ================================== Compiler Options: ==================================
@SuppressWarnings("rawtypes")
protected class CompilerOptions implements org.eclipse.objectteams.internal.jdt.nullity.NullCompilerOptions playedBy CompilerOptions
{
@SuppressWarnings("decapsulation")
void updateSeverity(int irritant, Object severityString) -> void updateSeverity(int irritant, Object severityString);
String getSeverityString(int irritant) -> String getSeverityString(int irritant);
public boolean isAnnotationBasedNullAnalysisEnabled;
/** 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;
/** Fully qualified name of annotation to use as marker for default nullable. */
public char[][] nullableByDefaultAnnotationName;
/** Fully qualified name of annotation to use as marker for default nonnull. */
public char[][] nonNullByDefaultAnnotationName;
/** Should null annotation types be emulated by synthetic bindings? */
public boolean emulateNullAnnotationTypes;
public long defaultNonNullness; // 0 or TagBits#AnnotationNullable or TagBits#AnnotationNonNull
String optionKeyFromIrritant(int irritant) <- replace String optionKeyFromIrritant(int irritant);
@SuppressWarnings("basecall")
static callin String optionKeyFromIrritant(int irritant) {
switch(irritant) {
case NullContractViolation :
return OPTION_ReportNullContractViolation;
case PotentialNullContractViolation :
return OPTION_ReportPotentialNullContractViolation;
case NullContractInsufficientInfo :
return OPTION_ReportNullContractInsufficientInfo;
default:
return base.optionKeyFromIrritant(irritant);
}
}
String warningTokenFromIrritant(int irritant) <- replace String warningTokenFromIrritant(int irritant);
@SuppressWarnings("basecall")
static callin String warningTokenFromIrritant(int irritant) {
switch(irritant) {
case NullContractViolation :
case PotentialNullContractViolation :
case NullContractInsufficientInfo :
return "nullcontract"; //$NON-NLS-1$
default:
return base.warningTokenFromIrritant(irritant);
}
}
warningTokenToIrritants <- replace warningTokenToIrritants;
@SuppressWarnings("basecall")
static callin IrritantSet warningTokenToIrritants(String string) {
if ("nullcontract".equals(string)) //$NON-NLS-1$
return new IrritantSet(NullContractViolation).set(PotentialNullContractViolation).set(NullContractInsufficientInfo);
return base.warningTokenToIrritants(string);
}
void getMap(Map optionsMap) <- after Map getMap()
with {optionsMap <- result}
@SuppressWarnings("unchecked")
private void getMap(Map optionsMap) {
optionsMap.put(OPTION_AnnotationBasedNullAnalysis, this.isAnnotationBasedNullAnalysisEnabled ? ENABLED : DISABLED);
if (this.isAnnotationBasedNullAnalysisEnabled) {
optionsMap.put(OPTION_ReportNullContractViolation, getSeverityString(NullContractViolation));
optionsMap.put(OPTION_ReportPotentialNullContractViolation, getSeverityString(PotentialNullContractViolation));
optionsMap.put(OPTION_ReportNullContractInsufficientInfo, getSeverityString(NullContractInsufficientInfo));
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));
}
if (this.nullableByDefaultAnnotationName != null) {
char[] compoundName = CharOperation.concatWith(this.nullableByDefaultAnnotationName, '.');
optionsMap.put(OPTION_NullableByDefaultAnnotationName, String.valueOf(compoundName));
}
if (this.nonNullByDefaultAnnotationName != null) {
char[] compoundName = CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.');
optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(compoundName));
}
optionsMap.put(OPTION_EmulateNullAnnotationTypes, this.emulateNullAnnotationTypes ? ENABLED : DISABLED);
if (this.defaultNonNullness == TagBits.AnnotationNullable)
optionsMap.put(OPTION_NullnessDefault, NULLABLE);
else if (this.defaultNonNullness == TagBits.AnnotationNonNull)
optionsMap.put(OPTION_NullnessDefault, NONNULL);
else
optionsMap.remove(OPTION_NullnessDefault);
}
}
void set(Map optionsMap) <- before void set(Map optionsMap);
private void set(Map optionsMap) {
Object optionValue;
if ((optionValue = optionsMap.get(OPTION_AnnotationBasedNullAnalysis)) != null) {
if (ENABLED.equals(optionValue)) {
this.isAnnotationBasedNullAnalysisEnabled = true;
// ensure that we actually have annotation names to use:
ensureNullAnnotationNames();
} else if (DISABLED.equals(optionValue)) {
this.isAnnotationBasedNullAnalysisEnabled = false;
}
}
if (this.isAnnotationBasedNullAnalysisEnabled) {
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_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_NullableByDefaultAnnotationName)) != null) {
this.nullableByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
}
if ((optionValue = optionsMap.get(OPTION_NonNullByDefaultAnnotationName)) != null) {
this.nonNullByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
}
if ((optionValue = optionsMap.get(OPTION_EmulateNullAnnotationTypes)) != null) {
if (ENABLED.equals(optionValue)) {
this.emulateNullAnnotationTypes = true;
} else if (DISABLED.equals(optionValue)) {
this.emulateNullAnnotationTypes = false;
}
}
if ((optionValue = optionsMap.get(OPTION_NullnessDefault)) != null) {
if (NULLABLE.equals(optionValue)) {
defaultNonNullness = TagBits.AnnotationNullable;
ensureNullAnnotationNames();
} else if (NONNULL.equals(optionValue)) {
defaultNonNullness = TagBits.AnnotationNonNull;
ensureNullAnnotationNames();
} else {
defaultNonNullness = 0;
}
}
}
}
private void ensureNullAnnotationNames() {
if (this.nullableAnnotationName == null)
this.nullableAnnotationName = NullCompilerOptions.DEFAULT_NULLABLE_ANNOTATION_NAME;
if (this.nonNullAnnotationName == null)
this.nonNullAnnotationName = NullCompilerOptions.DEFAULT_NONNULL_ANNOTATION_NAME;
if (this.nullableByDefaultAnnotationName == null)
this.nullableByDefaultAnnotationName = NullCompilerOptions.DEFAULT_NULLABLEBYDEFAULT_ANNOTATION_NAME;
if (this.nonNullByDefaultAnnotationName == null)
this.nonNullByDefaultAnnotationName = NullCompilerOptions.DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME;
}
}
@SuppressWarnings("rawtypes")
protected class JavaModelManager playedBy JavaModelManager {
JavaModelManager getJavaModelManager() -> JavaModelManager getJavaModelManager();
@SuppressWarnings("decapsulation")
protected HashSet getOptionNames() -> get HashSet optionNames;
IEclipsePreferences getInstancePreferences() -> IEclipsePreferences getInstancePreferences();
void internalStorePreference(String key, String value, IEclipsePreferences pref)
-> boolean storePreference(String key, String value, IEclipsePreferences pref);
public void storePreference(String key, String value) {
internalStorePreference(key, value, getInstancePreferences());
}
}
@SuppressWarnings({"rawtypes","unchecked"})
protected class JavaProject playedBy JavaProject {
void fillOptionNames() <- before Map getOptions(boolean inheritJavaCoreOptions);
private void fillOptionNames() {
JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
HashSet optionNames = javaModelManager.getOptionNames();
if (optionNames.contains(NullCompilerOptions.OPTION_AnnotationBasedNullAnalysis))
return;
optionNames.add(NullCompilerOptions.OPTION_AnnotationBasedNullAnalysis);
optionNames.add(NullCompilerOptions.OPTION_EmulateNullAnnotationTypes);
optionNames.add(NullCompilerOptions.OPTION_NonNullAnnotationName);
optionNames.add(NullCompilerOptions.OPTION_NullableAnnotationName);
optionNames.add(NullCompilerOptions.OPTION_NonNullByDefaultAnnotationName);
optionNames.add(NullCompilerOptions.OPTION_NullableByDefaultAnnotationName);
optionNames.add(NullCompilerOptions.OPTION_ReportNullContractInsufficientInfo);
optionNames.add(NullCompilerOptions.OPTION_ReportNullContractViolation);
optionNames.add(NullCompilerOptions.OPTION_ReportPotentialNullContractViolation);
optionNames.add(NullCompilerOptions.OPTION_NullnessDefault);
// also add to the instance preferences:
javaModelManager.storePreference(NullCompilerOptions.OPTION_NullableAnnotationName, String.valueOf(CharOperation.concatWith(NullCompilerOptions.DEFAULT_NULLABLE_ANNOTATION_NAME, '.')));
javaModelManager.storePreference(NullCompilerOptions.OPTION_NullableAnnotationName, String.valueOf(CharOperation.concatWith(NullCompilerOptions.DEFAULT_NULLABLE_ANNOTATION_NAME, '.')));
javaModelManager.storePreference(NullCompilerOptions.OPTION_NonNullAnnotationName, String.valueOf(CharOperation.concatWith(NullCompilerOptions.DEFAULT_NONNULL_ANNOTATION_NAME, '.')));
javaModelManager.storePreference(NullCompilerOptions.OPTION_NullableByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(NullCompilerOptions.DEFAULT_NULLABLEBYDEFAULT_ANNOTATION_NAME, '.')));
javaModelManager.storePreference(NullCompilerOptions.OPTION_NonNullByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(NullCompilerOptions.DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME, '.')));
}
}
}