/** * Check and fill in implicit annotations from overridden methods and from default. Precondition: * caller has checked whether annotation-based null analysis is enabled. */ public void checkImplicitNullAnnotations( MethodBinding currentMethod, AbstractMethodDeclaration srcMethod, boolean complain, Scope scope) { // check inherited nullness from superclass and superInterfaces try { ReferenceBinding currentType = currentMethod.declaringClass; if (currentType.id == TypeIds.T_JavaLangObject) { return; } boolean usesTypeAnnotations = scope.environment().usesNullTypeAnnotations(); boolean needToApplyReturnNonNullDefault = currentMethod.hasNonNullDefaultFor( Binding.DefaultLocationReturnType, usesTypeAnnotations); boolean needToApplyParameterNonNullDefault = currentMethod.hasNonNullDefaultFor(Binding.DefaultLocationParameter, usesTypeAnnotations); boolean needToApplyNonNullDefault = needToApplyReturnNonNullDefault | needToApplyParameterNonNullDefault; // compatibility & inheritance do not consider constructors / static methods: boolean isInstanceMethod = !currentMethod.isConstructor() && !currentMethod.isStatic(); complain &= isInstanceMethod; if (!needToApplyNonNullDefault && !complain && !(this.inheritNullAnnotations && isInstanceMethod)) { return; // short cut, no work to be done } if (isInstanceMethod) { List superMethodList = new ArrayList(); // need super types connected: if (currentType instanceof SourceTypeBinding && !currentType.isHierarchyConnected() && !currentType.isAnonymousType()) { ((SourceTypeBinding) currentType).scope.connectTypeHierarchy(); } int paramLen = currentMethod.parameters.length; findAllOverriddenMethods( currentMethod.original(), currentMethod.selector, paramLen, currentType, new HashSet(), superMethodList); // prepare interim storage for nullness info so we don't pollute currentMethod before we // know its conflict-free: InheritedNonNullnessInfo[] inheritedNonNullnessInfos = new InheritedNonNullnessInfo[paramLen + 1]; // index 0 is for the return type for (int i = 0; i < paramLen + 1; i++) inheritedNonNullnessInfos[i] = new InheritedNonNullnessInfo(); int length = superMethodList.size(); for (int i = length; --i >= 0; ) { MethodBinding currentSuper = (MethodBinding) superMethodList.get(i); if ((currentSuper.tagBits & TagBits.IsNullnessKnown) == 0) { // recurse to prepare currentSuper checkImplicitNullAnnotations( currentSuper, null, false, scope); // TODO (stephan) complain=true if currentSuper is source method?? } checkNullSpecInheritance( currentMethod, srcMethod, needToApplyReturnNonNullDefault, needToApplyParameterNonNullDefault, complain, currentSuper, null, scope, inheritedNonNullnessInfos); needToApplyNonNullDefault = false; } // transfer collected information into currentMethod: InheritedNonNullnessInfo info = inheritedNonNullnessInfos[0]; if (!info.complained) { long tagBits = 0; if (info.inheritedNonNullness == Boolean.TRUE) { tagBits = TagBits.AnnotationNonNull; } else if (info.inheritedNonNullness == Boolean.FALSE) { tagBits = TagBits.AnnotationNullable; } if (tagBits != 0) { if (!usesTypeAnnotations) { currentMethod.tagBits |= tagBits; } else { if (!currentMethod.returnType.isBaseType()) { LookupEnvironment env = scope.environment(); currentMethod.returnType = env.createAnnotatedType( currentMethod.returnType, env.nullAnnotationsFromTagBits(tagBits)); } } } } for (int i = 0; i < paramLen; i++) { info = inheritedNonNullnessInfos[i + 1]; if (!info.complained && info.inheritedNonNullness != null) { Argument currentArg = srcMethod == null ? null : srcMethod.arguments[i]; if (!usesTypeAnnotations) recordArgNonNullness( currentMethod, paramLen, i, currentArg, info.inheritedNonNullness); else recordArgNonNullness18( currentMethod, i, currentArg, info.inheritedNonNullness, scope.environment()); } } } if (needToApplyNonNullDefault) { if (!usesTypeAnnotations) currentMethod.fillInDefaultNonNullness(srcMethod); else currentMethod.fillInDefaultNonNullness18(srcMethod, scope.environment()); } } finally { currentMethod.tagBits |= TagBits.IsNullnessKnown; } }