Copyright (c) 2000, 2017 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Stephan Herrmann - Contributions for Bug 328281 - visibility leaks not detected when analyzing unused field in private class Bug 300576 - NPE Computing type hierarchy when compliance doesn't match libraries Bug 354536 - compiling package-info.java still depends on the order of compilation units Bug 349326 - [1.7] new warning for missing try-with-resources Bug 358903 - Filter practically unimportant resource leak warnings Bug 395977 - [compiler][resource] Resource leak warning behavior possibly incorrect for anonymous inner class Bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables Bug 427199 - [1.8][resource] avoid resource leak warnings on Streams that have no resource Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class Bug 444024 - [1.8][compiler][null] Type mismatch error in annotation generics assignment which happens "sometimes" Bug 459967 - [null] compiler should know about nullness of special methods like MyEnum.valueOf() Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for Bug 415821 - [1.8][compiler] CLASS_EXTENDS target type annotation missing for anonymous classes het@google.com - Bug 456986 - Bogus error when annotation processor generates annotation type Lars Vogel - Contributions for Bug 473178
/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for * Bug 328281 - visibility leaks not detected when analyzing unused field in private class * Bug 300576 - NPE Computing type hierarchy when compliance doesn't match libraries * Bug 354536 - compiling package-info.java still depends on the order of compilation units * Bug 349326 - [1.7] new warning for missing try-with-resources * Bug 358903 - Filter practically unimportant resource leak warnings * Bug 395977 - [compiler][resource] Resource leak warning behavior possibly incorrect for anonymous inner class * Bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. * Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables * Bug 427199 - [1.8][resource] avoid resource leak warnings on Streams that have no resource * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault * Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class * Bug 444024 - [1.8][compiler][null] Type mismatch error in annotation generics assignment which happens "sometimes" * Bug 459967 - [null] compiler should know about nullness of special methods like MyEnum.valueOf() * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for * Bug 415821 - [1.8][compiler] CLASS_EXTENDS target type annotation missing for anonymous classes * het@google.com - Bug 456986 - Bogus error when annotation processor generates annotation type * Lars Vogel <Lars.Vogel@vogella.com> - Contributions for * Bug 473178 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; @SuppressWarnings({"rawtypes"}) public class ClassScope extends Scope { public TypeDeclaration referenceContext; public TypeReference superTypeReference; java.util.ArrayList<Object> deferredBoundChecks; // contains TypeReference or Runnable. TODO consider making this a List<Runnable> public ClassScope(Scope parent, TypeDeclaration context) { super(Scope.CLASS_SCOPE, parent); this.referenceContext = context; this.deferredBoundChecks = null; // initialized if required } void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding supertype) { LocalTypeBinding anonymousType = buildLocalType(enclosingType, enclosingType.fPackage); anonymousType.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // tag all anonymous types as used locally int inheritedBits = supertype.typeBits; // for anonymous class assume same properties as its super (as a closeable) ... // ... unless it overrides close(): if ((inheritedBits & TypeIds.BitWrapperCloseable) != 0) { AbstractMethodDeclaration[] methods = this.referenceContext.methods; if (methods != null) { for (int i=0; i<methods.length; i++) { if (CharOperation.equals(TypeConstants.CLOSE, methods[i].selector) && methods[i].arguments == null) { inheritedBits &= TypeIds.InheritableBits; break; } } } } anonymousType.typeBits |= inheritedBits; if (supertype.isInterface()) { anonymousType.setSuperClass(getJavaLangObject()); anonymousType.setSuperInterfaces(new ReferenceBinding[] { supertype }); TypeReference typeReference = this.referenceContext.allocation.type; if (typeReference != null) { this.referenceContext.superInterfaces = new TypeReference[] { typeReference }; if ((supertype.tagBits & TagBits.HasDirectWildcard) != 0) { problemReporter().superTypeCannotUseWildcard(anonymousType, typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); } } } else { anonymousType.setSuperClass(supertype); anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); TypeReference typeReference = this.referenceContext.allocation.type; if (typeReference != null) { // no check for enum constant body this.referenceContext.superclass = typeReference; if (supertype.erasure().id == TypeIds.T_JavaLangEnum) { problemReporter().cannotExtendEnum(anonymousType, typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperClass(getJavaLangObject()); } else if (supertype.isFinal()) { problemReporter().anonymousClassCannotExtendFinalClass(typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperClass(getJavaLangObject()); } else if ((supertype.tagBits & TagBits.HasDirectWildcard) != 0) { problemReporter().superTypeCannotUseWildcard(anonymousType, typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperClass(getJavaLangObject()); } } } connectMemberTypes(); buildFieldsAndMethods(); anonymousType.faultInTypesForFieldsAndMethods(); anonymousType.verifyMethods(environment().methodVerifier()); } void buildFields() { SourceTypeBinding sourceType = this.referenceContext.binding; if (sourceType.areFieldsInitialized()) return; if (this.referenceContext.fields == null) { sourceType.setFields(Binding.NO_FIELDS); return; } // count the number of fields vs. initializers FieldDeclaration[] fields = this.referenceContext.fields; int size = fields.length; int count = 0; for (int i = 0; i < size; i++) { switch (fields[i].getKind()) { case AbstractVariableDeclaration.FIELD: case AbstractVariableDeclaration.ENUM_CONSTANT: count++; } } // iterate the field declarations to create the bindings, lose all duplicates FieldBinding[] fieldBindings = new FieldBinding[count]; HashtableOfObject knownFieldNames = new HashtableOfObject(count); count = 0; for (int i = 0; i < size; i++) { FieldDeclaration field = fields[i]; if (field.getKind() == AbstractVariableDeclaration.INITIALIZER) { // We used to report an error for initializers declared inside interfaces, but // now this error reporting is moved into the parser itself. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=212713 } else { FieldBinding fieldBinding = new FieldBinding(field, null, field.modifiers | ExtraCompilerModifiers.AccUnresolved, sourceType); fieldBinding.id = count; // field's type will be resolved when needed for top level types checkAndSetModifiersForField(fieldBinding, field); if (knownFieldNames.containsKey(field.name)) { FieldBinding previousBinding = (FieldBinding) knownFieldNames.get(field.name); if (previousBinding != null) { for (int f = 0; f < i; f++) { FieldDeclaration previousField = fields[f]; if (previousField.binding == previousBinding) { problemReporter().duplicateFieldInType(sourceType, previousField); break; } } } knownFieldNames.put(field.name, null); // ensure that the duplicate field is found & removed problemReporter().duplicateFieldInType(sourceType, field); field.binding = null; } else { knownFieldNames.put(field.name, fieldBinding); // remember that we have seen a field with this name fieldBindings[count++] = fieldBinding; } } } // remove duplicate fields if (count != fieldBindings.length) System.arraycopy(fieldBindings, 0, fieldBindings = new FieldBinding[count], 0, count); sourceType.tagBits &= ~(TagBits.AreFieldsSorted|TagBits.AreFieldsComplete); // in case some static imports reached already into this type sourceType.setFields(fieldBindings); } void buildFieldsAndMethods() { buildFields(); buildMethods(); SourceTypeBinding sourceType = this.referenceContext.binding; if (!sourceType.isPrivate() && sourceType.superclass instanceof SourceTypeBinding && sourceType.superclass.isPrivate()) ((SourceTypeBinding) sourceType.superclass).tagIndirectlyAccessibleMembers(); if (sourceType.isMemberType() && !sourceType.isLocalType()) ((MemberTypeBinding) sourceType).checkSyntheticArgsAndFields(); ReferenceBinding[] memberTypes = sourceType.memberTypes; for (int i = 0, length = memberTypes.length; i < length; i++) ((SourceTypeBinding) memberTypes[i]).scope.buildFieldsAndMethods(); } private LocalTypeBinding buildLocalType(SourceTypeBinding enclosingType, PackageBinding packageBinding) { this.referenceContext.scope = this; this.referenceContext.staticInitializerScope = new MethodScope(this, this.referenceContext, true); this.referenceContext.initializerScope = new MethodScope(this, this.referenceContext, false); // build the binding or the local type LocalTypeBinding localType = new LocalTypeBinding(this, enclosingType, innermostSwitchCase()); this.referenceContext.binding = localType; checkAndSetModifiers(); buildTypeVariables(); // Look at member types ReferenceBinding[] memberTypeBindings = Binding.NO_MEMBER_TYPES; if (this.referenceContext.memberTypes != null) { int size = this.referenceContext.memberTypes.length; memberTypeBindings = new ReferenceBinding[size]; int count = 0; nextMember : for (int i = 0; i < size; i++) { TypeDeclaration memberContext = this.referenceContext.memberTypes[i]; switch(TypeDeclaration.kind(memberContext.modifiers)) { case TypeDeclaration.INTERFACE_DECL : case TypeDeclaration.ANNOTATION_TYPE_DECL : problemReporter().illegalLocalTypeDeclaration(memberContext); continue nextMember; } ReferenceBinding type = localType; // check that the member does not conflict with an enclosing type do { if (CharOperation.equals(type.sourceName, memberContext.name)) { problemReporter().typeCollidesWithEnclosingType(memberContext); continue nextMember; } type = type.enclosingType(); } while (type != null); // check the member type does not conflict with another sibling member type for (int j = 0; j < i; j++) { if (CharOperation.equals(this.referenceContext.memberTypes[j].name, memberContext.name)) { problemReporter().duplicateNestedType(memberContext); continue nextMember; } } ClassScope memberScope = new ClassScope(this, this.referenceContext.memberTypes[i]); LocalTypeBinding memberBinding = memberScope.buildLocalType(localType, packageBinding); memberBinding.setAsMemberType(); memberTypeBindings[count++] = memberBinding; } if (count != size) System.arraycopy(memberTypeBindings, 0, memberTypeBindings = new ReferenceBinding[count], 0, count); } localType.setMemberTypes(memberTypeBindings); return localType; } void buildLocalTypeBinding(SourceTypeBinding enclosingType) { LocalTypeBinding localType = buildLocalType(enclosingType, enclosingType.fPackage); connectTypeHierarchy(); if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { checkParameterizedTypeBounds(); checkParameterizedSuperTypeCollisions(); } buildFieldsAndMethods(); localType.faultInTypesForFieldsAndMethods(); this.referenceContext.binding.verifyMethods(environment().methodVerifier()); } private void buildMemberTypes(AccessRestriction accessRestriction) { SourceTypeBinding sourceType = this.referenceContext.binding; ReferenceBinding[] memberTypeBindings = Binding.NO_MEMBER_TYPES; if (this.referenceContext.memberTypes != null) { int length = this.referenceContext.memberTypes.length; memberTypeBindings = new ReferenceBinding[length]; int count = 0; nextMember : for (int i = 0; i < length; i++) { TypeDeclaration memberContext = this.referenceContext.memberTypes[i]; if (this.environment().root.isProcessingAnnotations && this.environment().isMissingType(memberContext.name)) { throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the type } switch(TypeDeclaration.kind(memberContext.modifiers)) { case TypeDeclaration.INTERFACE_DECL : case TypeDeclaration.ANNOTATION_TYPE_DECL : if (sourceType.isNestedType() && sourceType.isClass() // no need to check for enum, since implicitly static && !sourceType.isStatic()) { problemReporter().illegalLocalTypeDeclaration(memberContext); continue nextMember; } break; } ReferenceBinding type = sourceType; // check that the member does not conflict with an enclosing type do { if (CharOperation.equals(type.sourceName, memberContext.name)) { problemReporter().typeCollidesWithEnclosingType(memberContext); continue nextMember; } type = type.enclosingType(); } while (type != null); // check that the member type does not conflict with another sibling member type for (int j = 0; j < i; j++) { if (CharOperation.equals(this.referenceContext.memberTypes[j].name, memberContext.name)) { problemReporter().duplicateNestedType(memberContext); continue nextMember; } } ClassScope memberScope = new ClassScope(this, memberContext); memberTypeBindings[count++] = memberScope.buildType(sourceType, sourceType.fPackage, accessRestriction); } if (count != length) System.arraycopy(memberTypeBindings, 0, memberTypeBindings = new ReferenceBinding[count], 0, count); } sourceType.setMemberTypes(memberTypeBindings); } void buildMethods() { SourceTypeBinding sourceType = this.referenceContext.binding; if (sourceType.areMethodsInitialized()) return; boolean isEnum = TypeDeclaration.kind(this.referenceContext.modifiers) == TypeDeclaration.ENUM_DECL; if (this.referenceContext.methods == null && !isEnum) { this.referenceContext.binding.setMethods(Binding.NO_METHODS); return; } // iterate the method declarations to create the bindings AbstractMethodDeclaration[] methods = this.referenceContext.methods; int size = methods == null ? 0 : methods.length; // look for <clinit> method int clinitIndex = -1; for (int i = 0; i < size; i++) { if (methods[i].isClinit()) { clinitIndex = i; break; } } int count = isEnum ? 2 : 0; // reserve 2 slots for special enum methods: #values() and #valueOf(String) MethodBinding[] methodBindings = new MethodBinding[(clinitIndex == -1 ? size : size - 1) + count]; // create special methods for enums if (isEnum) { methodBindings[0] = sourceType.addSyntheticEnumMethod(TypeConstants.VALUES); // add <EnumType>[] values() methodBindings[1] = sourceType.addSyntheticEnumMethod(TypeConstants.VALUEOF); // add <EnumType> valueOf() } // create bindings for source methods boolean hasNativeMethods = false; if (sourceType.isAbstract()) { for (int i = 0; i < size; i++) { if (i != clinitIndex) { MethodScope scope = new MethodScope(this, methods[i], false); MethodBinding methodBinding = scope.createMethod(methods[i]); if (methodBinding != null) { // is null if binding could not be created methodBindings[count++] = methodBinding; hasNativeMethods = hasNativeMethods || methodBinding.isNative(); } } } } else { boolean hasAbstractMethods = false; for (int i = 0; i < size; i++) { if (i != clinitIndex) { MethodScope scope = new MethodScope(this, methods[i], false); MethodBinding methodBinding = scope.createMethod(methods[i]); if (methodBinding != null) { // is null if binding could not be created methodBindings[count++] = methodBinding; hasAbstractMethods = hasAbstractMethods || methodBinding.isAbstract(); hasNativeMethods = hasNativeMethods || methodBinding.isNative(); } } } if (hasAbstractMethods) problemReporter().abstractMethodInConcreteClass(sourceType); } if (count != methodBindings.length) System.arraycopy(methodBindings, 0, methodBindings = new MethodBinding[count], 0, count); sourceType.tagBits &= ~(TagBits.AreMethodsSorted|TagBits.AreMethodsComplete); // in case some static imports reached already into this type sourceType.setMethods(methodBindings); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=243917, conservatively tag all methods and fields as // being in use if there is a native method in the class. if (hasNativeMethods) { for (int i = 0; i < methodBindings.length; i++) { methodBindings[i].modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } FieldBinding[] fields = sourceType.unResolvedFields(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=301683 for (int i = 0; i < fields.length; i++) { fields[i].modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } if (isEnum && compilerOptions().isAnnotationBasedNullAnalysisEnabled) { // mark return types of values & valueOf as nonnull (needed to wait till after setMethods() to avoid reentrance): LookupEnvironment environment = this.environment(); ((SyntheticMethodBinding)methodBindings[0]).markNonNull(environment); ((SyntheticMethodBinding)methodBindings[1]).markNonNull(environment); } } SourceTypeBinding buildType(SourceTypeBinding enclosingType, PackageBinding packageBinding, AccessRestriction accessRestriction) { // provide the typeDeclaration with needed scopes this.referenceContext.scope = this; this.referenceContext.staticInitializerScope = new MethodScope(this, this.referenceContext, true); this.referenceContext.initializerScope = new MethodScope(this, this.referenceContext, false); if (enclosingType == null) { char[][] className = CharOperation.arrayConcat(packageBinding.compoundName, this.referenceContext.name); this.referenceContext.binding = new SourceTypeBinding(className, packageBinding, this); } else { char[][] className = CharOperation.deepCopy(enclosingType.compoundName); className[className.length - 1] = CharOperation.concat(className[className.length - 1], this.referenceContext.name, '$'); if (packageBinding.hasType0Any(className[className.length - 1])) { // report the error against the parent - its still safe to answer the member type this.parent.problemReporter().duplicateNestedType(this.referenceContext); } this.referenceContext.binding = new MemberTypeBinding(className, this, enclosingType); } SourceTypeBinding sourceType = this.referenceContext.binding; sourceType.module = module(); environment().setAccessRestriction(sourceType, accessRestriction); TypeParameter[] typeParameters = this.referenceContext.typeParameters; sourceType.typeVariables = typeParameters == null || typeParameters.length == 0 ? Binding.NO_TYPE_VARIABLES : null; sourceType.fPackage.addType(sourceType); checkAndSetModifiers(); buildTypeVariables(); buildMemberTypes(accessRestriction); return sourceType; } private void buildTypeVariables() { SourceTypeBinding sourceType = this.referenceContext.binding; TypeParameter[] typeParameters = this.referenceContext.typeParameters; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, If they exist at all, process type parameters irrespective of source level. if (typeParameters == null || typeParameters.length == 0) { sourceType.setTypeVariables(Binding.NO_TYPE_VARIABLES); return; } sourceType.setTypeVariables(Binding.NO_TYPE_VARIABLES); // safety if (sourceType.id == TypeIds.T_JavaLangObject) { // handle the case of redefining java.lang.Object up front problemReporter().objectCannotBeGeneric(this.referenceContext); return; } sourceType.setTypeVariables(createTypeVariables(typeParameters, sourceType)); sourceType.modifiers |= ExtraCompilerModifiers.AccGenericSignature; } @Override void resolveTypeParameter(TypeParameter typeParameter) { typeParameter.resolve(this); } private void checkAndSetModifiers() { SourceTypeBinding sourceType = this.referenceContext.binding; int modifiers = sourceType.modifiers; if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) problemReporter().duplicateModifierForType(sourceType); ReferenceBinding enclosingType = sourceType.enclosingType(); boolean isMemberType = sourceType.isMemberType(); if (isMemberType) { if (sourceType.hasEnclosingInstanceContext()) modifiers |= (enclosingType.modifiers & ExtraCompilerModifiers.AccGenericSignature); modifiers |= (enclosingType.modifiers & ClassFileConstants.AccStrictfp); // checks for member types before local types to catch local members if (enclosingType.isInterface()) modifiers |= ClassFileConstants.AccPublic; if (sourceType.isEnum()) { if (!enclosingType.isStatic()) problemReporter().nonStaticContextForEnumMemberType(sourceType); else modifiers |= ClassFileConstants.AccStatic; } else if (sourceType.isInterface()) { modifiers |= ClassFileConstants.AccStatic; // 8.5.1 } } else if (sourceType.isLocalType()) { if (sourceType.isEnum()) { problemReporter().illegalLocalTypeDeclaration(this.referenceContext); sourceType.modifiers = 0; return; } if (sourceType.isAnonymousType()) { if (compilerOptions().complianceLevel < ClassFileConstants.JDK9) modifiers |= ClassFileConstants.AccFinal; // set AccEnum flag for anonymous body of enum constants if (this.referenceContext.allocation.type == null) modifiers |= ClassFileConstants.AccEnum; } Scope scope = this; do { switch (scope.kind) { case METHOD_SCOPE : MethodScope methodScope = (MethodScope) scope; if (methodScope.isLambdaScope()) methodScope = methodScope.namedMethodScope(); if (methodScope.isInsideInitializer()) { SourceTypeBinding type = ((TypeDeclaration) methodScope.referenceContext).binding; // inside field declaration ? check field modifier to see if deprecated if (methodScope.initializedField != null) { // currently inside this field initialization if (methodScope.initializedField.isViewedAsDeprecated() && !sourceType.isDeprecated()) modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; } else { if (type.isStrictfp()) modifiers |= ClassFileConstants.AccStrictfp; if (type.isViewedAsDeprecated() && !sourceType.isDeprecated()) modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; } } else { MethodBinding method = ((AbstractMethodDeclaration) methodScope.referenceContext).binding; if (method != null) { if (method.isStrictfp()) modifiers |= ClassFileConstants.AccStrictfp; if (method.isViewedAsDeprecated() && !sourceType.isDeprecated()) modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; } } break; case CLASS_SCOPE : // local member if (enclosingType.isStrictfp()) modifiers |= ClassFileConstants.AccStrictfp; if (enclosingType.isViewedAsDeprecated() && !sourceType.isDeprecated()) { modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; sourceType.tagBits |= enclosingType.tagBits & TagBits.AnnotationTerminallyDeprecated; } break; } scope = scope.parent; } while (scope != null); } // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; if ((realModifiers & ClassFileConstants.AccInterface) != 0) { // interface and annotation type // detect abnormal cases for interfaces if (isMemberType) { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract | ClassFileConstants.AccInterface | ClassFileConstants.AccStrictfp | ClassFileConstants.AccAnnotation); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { if ((realModifiers & ClassFileConstants.AccAnnotation) != 0) problemReporter().illegalModifierForAnnotationMemberType(sourceType); else problemReporter().illegalModifierForMemberInterface(sourceType); } /* } else if (sourceType.isLocalType()) { //interfaces cannot be defined inside a method int unexpectedModifiers = ~(AccAbstract | AccInterface | AccStrictfp); if ((realModifiers & unexpectedModifiers) != 0) problemReporter().illegalModifierForLocalInterface(sourceType); */ } else { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ClassFileConstants.AccInterface | ClassFileConstants.AccStrictfp | ClassFileConstants.AccAnnotation); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { if ((realModifiers & ClassFileConstants.AccAnnotation) != 0) problemReporter().illegalModifierForAnnotationType(sourceType); else problemReporter().illegalModifierForInterface(sourceType); } } /* * AccSynthetic must be set if the target is greater than 1.5. 1.5 VM don't support AccSynthetics flag. */ if (sourceType.sourceName == TypeConstants.PACKAGE_INFO_NAME && compilerOptions().targetJDK > ClassFileConstants.JDK1_5) { modifiers |= ClassFileConstants.AccSynthetic; } modifiers |= ClassFileConstants.AccAbstract; } else if ((realModifiers & ClassFileConstants.AccEnum) != 0) { // detect abnormal cases for enums if (isMemberType) { // includes member types defined inside local types final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { problemReporter().illegalModifierForMemberEnum(sourceType); modifiers &= ~ClassFileConstants.AccAbstract; // avoid leaking abstract modifier realModifiers &= ~ClassFileConstants.AccAbstract; // modifiers &= ~(realModifiers & UNEXPECTED_MODIFIERS); // realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; } } else if (sourceType.isLocalType()) { // each enum constant is an anonymous local type and its modifiers were already checked as an enum constant field } else { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) problemReporter().illegalModifierForEnum(sourceType); } if (!sourceType.isAnonymousType()) { checkAbstractEnum: { // does define abstract methods ? if ((this.referenceContext.bits & ASTNode.HasAbstractMethods) != 0) { modifiers |= ClassFileConstants.AccAbstract; break checkAbstractEnum; } // body of enum constant must implement any inherited abstract methods // enum type needs to implement abstract methods if one of its constants does not supply a body TypeDeclaration typeDeclaration = this.referenceContext; FieldDeclaration[] fields = typeDeclaration.fields; int fieldsLength = fields == null ? 0 : fields.length; if (fieldsLength == 0) break checkAbstractEnum; // has no constants so must implement the method itself AbstractMethodDeclaration[] methods = typeDeclaration.methods; int methodsLength = methods == null ? 0 : methods.length; // TODO (kent) cannot tell that the superinterfaces are empty or that their methods are implemented boolean definesAbstractMethod = typeDeclaration.superInterfaces != null; for (int i = 0; i < methodsLength && !definesAbstractMethod; i++) definesAbstractMethod = methods[i].isAbstract(); if (!definesAbstractMethod) break checkAbstractEnum; // all methods have bodies boolean needAbstractBit = false; for (int i = 0; i < fieldsLength; i++) { FieldDeclaration fieldDecl = fields[i]; if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { if (fieldDecl.initialization instanceof QualifiedAllocationExpression) { needAbstractBit = true; } else { break checkAbstractEnum; } } } // tag this enum as abstract since an abstract method must be implemented AND all enum constants define an anonymous body // as a result, each of its anonymous constants will see it as abstract and must implement each inherited abstract method if (needAbstractBit) { modifiers |= ClassFileConstants.AccAbstract; } } // final if no enum constant with anonymous body checkFinalEnum: { TypeDeclaration typeDeclaration = this.referenceContext; FieldDeclaration[] fields = typeDeclaration.fields; if (fields != null) { for (int i = 0, fieldsLength = fields.length; i < fieldsLength; i++) { FieldDeclaration fieldDecl = fields[i]; if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { if (fieldDecl.initialization instanceof QualifiedAllocationExpression) { break checkFinalEnum; } } } } modifiers |= ClassFileConstants.AccFinal; } } } else { // detect abnormal cases for classes if (isMemberType) { // includes member types defined inside local types final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) problemReporter().illegalModifierForMemberClass(sourceType); } else if (sourceType.isLocalType()) { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) problemReporter().illegalModifierForLocalClass(sourceType); } else { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) problemReporter().illegalModifierForClass(sourceType); } // check that Final and Abstract are not set together if ((realModifiers & (ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract)) == (ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract)) problemReporter().illegalModifierCombinationFinalAbstractForClass(sourceType); } if (isMemberType) { // test visibility modifiers inconsistency, isolate the accessors bits if (enclosingType.isInterface()) { if ((realModifiers & (ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate)) != 0) { problemReporter().illegalVisibilityModifierForInterfaceMemberType(sourceType); // need to keep the less restrictive if ((realModifiers & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((realModifiers & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } } else { int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) > 1) { problemReporter().illegalVisibilityModifierCombinationForMemberType(sourceType); // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.AccPrivate; } } } // static modifier test if ((realModifiers & ClassFileConstants.AccStatic) == 0) { if (enclosingType.isInterface()) modifiers |= ClassFileConstants.AccStatic; } else if (!enclosingType.isStatic()) { // error the enclosing type of a static field must be static or a top-level type problemReporter().illegalStaticModifierForMemberType(sourceType); } } sourceType.modifiers = modifiers; } /* This method checks the modifiers of a field. * * 9.3 & 8.3 * Need to integrate the check for the final modifiers for nested types * * Note : A scope is accessible by : fieldBinding.declaringClass.scope */ private void checkAndSetModifiersForField(FieldBinding fieldBinding, FieldDeclaration fieldDecl) { int modifiers = fieldBinding.modifiers; final ReferenceBinding declaringClass = fieldBinding.declaringClass; if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) problemReporter().duplicateModifierForField(declaringClass, fieldDecl); if (declaringClass.isInterface()) { final int IMPLICIT_MODIFIERS = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; // set the modifiers modifiers |= IMPLICIT_MODIFIERS; // and then check that they are the only ones if ((modifiers & ExtraCompilerModifiers.AccJustFlag) != IMPLICIT_MODIFIERS) { if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0) problemReporter().illegalModifierForAnnotationField(fieldDecl); else problemReporter().illegalModifierForInterfaceField(fieldDecl); } fieldBinding.modifiers = modifiers; return; } else if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { // check that they are not modifiers in source if ((modifiers & ExtraCompilerModifiers.AccJustFlag) != 0) problemReporter().illegalModifierForEnumConstant(declaringClass, fieldDecl); // set the modifiers // https://bugs.eclipse.org/bugs/show_bug.cgi?id=267670. Force all enumerators to be marked // as used locally. We are unable to track the usage of these reliably as they could be used // in non obvious ways via the synthesized methods values() and valueOf(String) or by using // Enum.valueOf(Class<T>, String). final int IMPLICIT_MODIFIERS = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccEnum | ExtraCompilerModifiers.AccLocallyUsed; fieldBinding.modifiers|= IMPLICIT_MODIFIERS; return; } // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccFinal | ClassFileConstants.AccStatic | ClassFileConstants.AccTransient | ClassFileConstants.AccVolatile); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { problemReporter().illegalModifierForField(declaringClass, fieldDecl); modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_MODIFIERS; } int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) > 1) { problemReporter().illegalVisibilityModifierCombinationForField(declaringClass, fieldDecl); // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.AccPrivate; } } if ((realModifiers & (ClassFileConstants.AccFinal | ClassFileConstants.AccVolatile)) == (ClassFileConstants.AccFinal | ClassFileConstants.AccVolatile)) problemReporter().illegalModifierCombinationFinalVolatileForField(declaringClass, fieldDecl); if (fieldDecl.initialization == null && (modifiers & ClassFileConstants.AccFinal) != 0) modifiers |= ExtraCompilerModifiers.AccBlankFinal; fieldBinding.modifiers = modifiers; } public void checkParameterizedSuperTypeCollisions() { // check for parameterized interface collisions (when different parameterizations occur) SourceTypeBinding sourceType = this.referenceContext.binding; ReferenceBinding[] interfaces = sourceType.superInterfaces; Map invocations = new HashMap(2); ReferenceBinding itsSuperclass = sourceType.isInterface() ? null : sourceType.superclass; nextInterface: for (int i = 0, length = interfaces.length; i < length; i++) { ReferenceBinding one = interfaces[i]; if (one == null) continue nextInterface; if (itsSuperclass != null && hasErasedCandidatesCollisions(itsSuperclass, one, invocations, sourceType, this.referenceContext)) continue nextInterface; nextOtherInterface: for (int j = 0; j < i; j++) { ReferenceBinding two = interfaces[j]; if (two == null) continue nextOtherInterface; if (hasErasedCandidatesCollisions(one, two, invocations, sourceType, this.referenceContext)) continue nextInterface; } } TypeParameter[] typeParameters = this.referenceContext.typeParameters; nextVariable : for (int i = 0, paramLength = typeParameters == null ? 0 : typeParameters.length; i < paramLength; i++) { TypeParameter typeParameter = typeParameters[i]; TypeVariableBinding typeVariable = typeParameter.binding; if (typeVariable == null || !typeVariable.isValidBinding()) continue nextVariable; TypeReference[] boundRefs = typeParameter.bounds; if (boundRefs != null) { boolean checkSuperclass = TypeBinding.equalsEquals(typeVariable.firstBound, typeVariable.superclass); for (int j = 0, boundLength = boundRefs.length; j < boundLength; j++) { TypeReference typeRef = boundRefs[j]; TypeBinding superType = typeRef.resolvedType; if (superType == null || !superType.isValidBinding()) continue; // check against superclass if (checkSuperclass) if (hasErasedCandidatesCollisions(superType, typeVariable.superclass, invocations, typeVariable, typeRef)) continue nextVariable; // check against superinterfaces for (int index = typeVariable.superInterfaces.length; --index >= 0;) if (hasErasedCandidatesCollisions(superType, typeVariable.superInterfaces[index], invocations, typeVariable, typeRef)) continue nextVariable; } } } ReferenceBinding[] memberTypes = this.referenceContext.binding.memberTypes; if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) for (int i = 0, size = memberTypes.length; i < size; i++) ((SourceTypeBinding) memberTypes[i]).scope.checkParameterizedSuperTypeCollisions(); } private void checkForInheritedMemberTypes(SourceTypeBinding sourceType) { // search up the hierarchy of the sourceType to see if any superType defines a member type // when no member types are defined, tag the sourceType & each superType with the HasNoMemberTypes bit // assumes super types have already been checked & tagged ReferenceBinding currentType = sourceType; ReferenceBinding[] interfacesToVisit = null; int nextPosition = 0; do { if (currentType.hasMemberTypes()) // avoid resolving member types eagerly return; ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); // in code assist cases when source types are added late, may not be finished connecting hierarchy if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { if (interfacesToVisit == null) { interfacesToVisit = itsInterfaces; nextPosition = interfacesToVisit.length; } else { int itsLength = itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface : for (int a = 0; a < itsLength; a++) { ReferenceBinding next = itsInterfaces[a]; for (int b = 0; b < nextPosition; b++) if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface; interfacesToVisit[nextPosition++] = next; } } } } while ((currentType = currentType.superclass()) != null && (currentType.tagBits & TagBits.HasNoMemberTypes) == 0); if (interfacesToVisit != null) { // contains the interfaces between the sourceType and any superclass, which was tagged as having no member types boolean needToTag = false; for (int i = 0; i < nextPosition; i++) { ReferenceBinding anInterface = interfacesToVisit[i]; if ((anInterface.tagBits & TagBits.HasNoMemberTypes) == 0) { // skip interface if it already knows it has no member types if (anInterface.hasMemberTypes()) // avoid resolving member types eagerly return; needToTag = true; ReferenceBinding[] itsInterfaces = anInterface.superInterfaces(); if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { int itsLength = itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface : for (int a = 0; a < itsLength; a++) { ReferenceBinding next = itsInterfaces[a]; for (int b = 0; b < nextPosition; b++) if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface; interfacesToVisit[nextPosition++] = next; } } } } if (needToTag) { for (int i = 0; i < nextPosition; i++) interfacesToVisit[i].tagBits |= TagBits.HasNoMemberTypes; } } // tag the sourceType and all of its superclasses, unless they have already been tagged currentType = sourceType; do { currentType.tagBits |= TagBits.HasNoMemberTypes; } while ((currentType = currentType.superclass()) != null && (currentType.tagBits & TagBits.HasNoMemberTypes) == 0); } // Perform deferred bound checks for parameterized type references (only done after hierarchy is connected) public void checkParameterizedTypeBounds() { for (int i = 0, l = this.deferredBoundChecks == null ? 0 : this.deferredBoundChecks.size(); i < l; i++) { Object toCheck = this.deferredBoundChecks.get(i); if (toCheck instanceof TypeReference) ((TypeReference) toCheck).checkBounds(this); else if (toCheck instanceof Runnable) ((Runnable) toCheck).run(); } this.deferredBoundChecks = null; ReferenceBinding[] memberTypes = this.referenceContext.binding.memberTypes; if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) for (int i = 0, size = memberTypes.length; i < size; i++) ((SourceTypeBinding) memberTypes[i]).scope.checkParameterizedTypeBounds(); } private void connectMemberTypes() { SourceTypeBinding sourceType = this.referenceContext.binding; ReferenceBinding[] memberTypes = sourceType.memberTypes; if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) { for (int i = 0, size = memberTypes.length; i < size; i++) ((SourceTypeBinding) memberTypes[i]).scope.connectTypeHierarchy(); } } /* Our current belief based on available JCK tests is: inherited member types are visible as a potential superclass. inherited interfaces are not visible when defining a superinterface. Error recovery story: ensure the superclass is set to java.lang.Object if a problem is detected resolving the superclass. Answer false if an error was reported against the sourceType. */ private boolean connectSuperclass() { SourceTypeBinding sourceType = this.referenceContext.binding; if (sourceType.id == TypeIds.T_JavaLangObject) { // handle the case of redefining java.lang.Object up front sourceType.setSuperClass(null); sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); if (!sourceType.isClass()) problemReporter().objectMustBeClass(sourceType); if (this.referenceContext.superclass != null || (this.referenceContext.superInterfaces != null && this.referenceContext.superInterfaces.length > 0)) problemReporter().objectCannotHaveSuperTypes(sourceType); return true; // do not propagate Object's hierarchy problems down to every subtype } if (this.referenceContext.superclass == null) { if (sourceType.isEnum() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) // do not connect if source < 1.5 as enum already got flagged as syntax error return connectEnumSuperclass(); sourceType.setSuperClass(getJavaLangObject()); return !detectHierarchyCycle(sourceType, sourceType.superclass, null); } TypeReference superclassRef = this.referenceContext.superclass; ReferenceBinding superclass = findSupertype(superclassRef); if (superclass != null) { // is null if a cycle was detected cycle or a problem if (!superclass.isClass() && (superclass.tagBits & TagBits.HasMissingType) == 0) { problemReporter().superclassMustBeAClass(sourceType, superclassRef, superclass); } else if (superclass.isFinal()) { problemReporter().classExtendFinalClass(sourceType, superclassRef, superclass); } else if ((superclass.tagBits & TagBits.HasDirectWildcard) != 0) { problemReporter().superTypeCannotUseWildcard(sourceType, superclassRef, superclass); } else if (superclass.erasure().id == TypeIds.T_JavaLangEnum) { problemReporter().cannotExtendEnum(sourceType, superclassRef, superclass); } else if ((superclass.tagBits & TagBits.HierarchyHasProblems) != 0 || !superclassRef.resolvedType.isValidBinding()) { sourceType.setSuperClass(superclass); sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype return superclassRef.resolvedType.isValidBinding(); // reported some error against the source type ? } else { // only want to reach here when no errors are reported sourceType.setSuperClass(superclass); sourceType.typeBits |= (superclass.typeBits & TypeIds.InheritableBits); // further analysis against white lists for the unlikely case we are compiling java.io.*: if ((sourceType.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) sourceType.typeBits |= sourceType.applyCloseableClassWhitelists(); return true; } } sourceType.tagBits |= TagBits.HierarchyHasProblems; sourceType.setSuperClass(getJavaLangObject()); if ((sourceType.superclass.tagBits & TagBits.BeginHierarchyCheck) == 0) detectHierarchyCycle(sourceType, sourceType.superclass, null); return false; // reported some error against the source type }
enum X (implicitly) extends Enum
/** * enum X (implicitly) extends Enum<X> */
private boolean connectEnumSuperclass() { SourceTypeBinding sourceType = this.referenceContext.binding; ReferenceBinding rootEnumType = getJavaLangEnum(); if ((rootEnumType.tagBits & TagBits.HasMissingType) != 0) { sourceType.tagBits |= TagBits.HierarchyHasProblems; // mark missing supertpye sourceType.setSuperClass(rootEnumType); return false; } boolean foundCycle = detectHierarchyCycle(sourceType, rootEnumType, null); // arity check for well-known Enum<E> TypeVariableBinding[] refTypeVariables = rootEnumType.typeVariables(); if (refTypeVariables == Binding.NO_TYPE_VARIABLES) { // check generic problemReporter().nonGenericTypeCannotBeParameterized(0, null, rootEnumType, new TypeBinding[]{ sourceType }); return false; // cannot reach here as AbortCompilation is thrown } else if (1 != refTypeVariables.length) { // check arity problemReporter().incorrectArityForParameterizedType(null, rootEnumType, new TypeBinding[]{ sourceType }); return false; // cannot reach here as AbortCompilation is thrown } // check argument type compatibility ParameterizedTypeBinding superType = environment().createParameterizedType( rootEnumType, new TypeBinding[]{ environment().convertToRawType(sourceType, false /*do not force conversion of enclosing types*/), } , null); sourceType.tagBits |= (superType.tagBits & TagBits.HierarchyHasProblems); // propagate if missing supertpye sourceType.setSuperClass(superType); // bound check (in case of bogus definition of Enum type) if (!refTypeVariables[0].boundCheck(superType, sourceType, this, null).isOKbyJLS()) { problemReporter().typeMismatchError(rootEnumType, refTypeVariables[0], sourceType, null); } return !foundCycle; } /* Our current belief based on available JCK 1.3 tests is: inherited member types are visible as a potential superclass. inherited interfaces are visible when defining a superinterface. Error recovery story: ensure the superinterfaces contain only valid visible interfaces. Answer false if an error was reported against the sourceType. */ private boolean connectSuperInterfaces() { SourceTypeBinding sourceType = this.referenceContext.binding; sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); if (this.referenceContext.superInterfaces == null) { if (sourceType.isAnnotationType() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // do not connect if source < 1.5 as annotation already got flagged as syntax error) { ReferenceBinding annotationType = getJavaLangAnnotationAnnotation(); boolean foundCycle = detectHierarchyCycle(sourceType, annotationType, null); sourceType.setSuperInterfaces(new ReferenceBinding[] { annotationType }); return !foundCycle; } return true; } if (sourceType.id == TypeIds.T_JavaLangObject) // already handled the case of redefining java.lang.Object return true; boolean noProblems = true; int length = this.referenceContext.superInterfaces.length; ReferenceBinding[] interfaceBindings = new ReferenceBinding[length]; int count = 0; nextInterface : for (int i = 0; i < length; i++) { TypeReference superInterfaceRef = this.referenceContext.superInterfaces[i]; ReferenceBinding superInterface = findSupertype(superInterfaceRef); if (superInterface == null) { // detected cycle sourceType.tagBits |= TagBits.HierarchyHasProblems; noProblems = false; continue nextInterface; } // check for simple interface collisions // Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I) for (int j = 0; j < i; j++) { if (TypeBinding.equalsEquals(interfaceBindings[j], superInterface)) { problemReporter().duplicateSuperinterface(sourceType, superInterfaceRef, superInterface); sourceType.tagBits |= TagBits.HierarchyHasProblems; noProblems = false; continue nextInterface; } } if (!superInterface.isInterface() && (superInterface.tagBits & TagBits.HasMissingType) == 0) { problemReporter().superinterfaceMustBeAnInterface(sourceType, superInterfaceRef, superInterface); sourceType.tagBits |= TagBits.HierarchyHasProblems; noProblems = false; continue nextInterface; } else if (superInterface.isAnnotationType()){ problemReporter().annotationTypeUsedAsSuperinterface(sourceType, superInterfaceRef, superInterface); } if ((superInterface.tagBits & TagBits.HasDirectWildcard) != 0) { problemReporter().superTypeCannotUseWildcard(sourceType, superInterfaceRef, superInterface); sourceType.tagBits |= TagBits.HierarchyHasProblems; noProblems = false; continue nextInterface; } if ((superInterface.tagBits & TagBits.HierarchyHasProblems) != 0 || !superInterfaceRef.resolvedType.isValidBinding()) { sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype noProblems &= superInterfaceRef.resolvedType.isValidBinding(); } // only want to reach here when no errors are reported sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits); // further analysis against white lists for the unlikely case we are compiling java.util.stream.Stream: if ((sourceType.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) sourceType.typeBits |= sourceType.applyCloseableInterfaceWhitelists(); interfaceBindings[count++] = superInterface; } // hold onto all correctly resolved superinterfaces if (count > 0) { if (count != length) System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[count], 0, count); sourceType.setSuperInterfaces(interfaceBindings); } return noProblems; } void connectTypeHierarchy() { SourceTypeBinding sourceType = this.referenceContext.binding; CompilationUnitScope compilationUnitScope = compilationUnitScope(); boolean wasAlreadyConnecting = compilationUnitScope.connectingHierarchy; compilationUnitScope.connectingHierarchy = true; try { if ((sourceType.tagBits & TagBits.BeginHierarchyCheck) == 0) { sourceType.tagBits |= TagBits.BeginHierarchyCheck; environment().typesBeingConnected.add(sourceType); boolean noProblems = connectSuperclass(); noProblems &= connectSuperInterfaces(); environment().typesBeingConnected.remove(sourceType); sourceType.tagBits |= TagBits.EndHierarchyCheck; noProblems &= connectTypeVariables(this.referenceContext.typeParameters, false); sourceType.tagBits |= TagBits.TypeVariablesAreConnected; if (noProblems && sourceType.isHierarchyInconsistent()) problemReporter().hierarchyHasProblems(sourceType); } connectMemberTypes(); } finally { compilationUnitScope.connectingHierarchy = wasAlreadyConnecting; } LookupEnvironment env = environment(); try { env.missingClassFileLocation = this.referenceContext; checkForInheritedMemberTypes(sourceType); } catch (AbortCompilation e) { e.updateContext(this.referenceContext, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } @Override public boolean deferCheck(Runnable check) { if (compilationUnitScope().connectingHierarchy) { if (this.deferredBoundChecks == null) this.deferredBoundChecks = new ArrayList<>(); this.deferredBoundChecks.add(check); return true; } else { return false; } } private void connectTypeHierarchyWithoutMembers() { // must ensure the imports are resolved if (this.parent instanceof CompilationUnitScope) { if (((CompilationUnitScope) this.parent).imports == null) ((CompilationUnitScope) this.parent).checkAndSetImports(); } else if (this.parent instanceof ClassScope) { // ensure that the enclosing type has already been checked ((ClassScope) this.parent).connectTypeHierarchyWithoutMembers(); } // double check that the hierarchy search has not already begun... SourceTypeBinding sourceType = this.referenceContext.binding; if ((sourceType.tagBits & TagBits.BeginHierarchyCheck) != 0) return; CompilationUnitScope compilationUnitScope = compilationUnitScope(); boolean wasAlreadyConnecting = compilationUnitScope.connectingHierarchy; compilationUnitScope.connectingHierarchy = true; try { sourceType.tagBits |= TagBits.BeginHierarchyCheck; environment().typesBeingConnected.add(sourceType); boolean noProblems = connectSuperclass(); noProblems &= connectSuperInterfaces(); environment().typesBeingConnected.remove(sourceType); sourceType.tagBits |= TagBits.EndHierarchyCheck; noProblems &= connectTypeVariables(this.referenceContext.typeParameters, false); sourceType.tagBits |= TagBits.TypeVariablesAreConnected; if (noProblems && sourceType.isHierarchyInconsistent()) problemReporter().hierarchyHasProblems(sourceType); } finally { compilationUnitScope.connectingHierarchy = wasAlreadyConnecting; } } public boolean detectHierarchyCycle(TypeBinding superType, TypeReference reference) { if (!(superType instanceof ReferenceBinding)) return false; if (reference == this.superTypeReference) { // see findSuperType() if (superType.isTypeVariable()) return false; // error case caught in resolveSuperType() // abstract class X<K,V> implements java.util.Map<K,V> // static abstract class M<K,V> implements Entry<K,V> if (superType.isParameterizedType()) superType = ((ParameterizedTypeBinding) superType).genericType(); compilationUnitScope().recordSuperTypeReference(superType); // to record supertypes return detectHierarchyCycle(this.referenceContext.binding, (ReferenceBinding) superType, reference); } // Reinstate the code deleted by the fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=205235 // For details, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=294057. if ((superType.tagBits & TagBits.BeginHierarchyCheck) == 0 && superType instanceof SourceTypeBinding) // ensure if this is a source superclass that it has already been checked ((SourceTypeBinding) superType).scope.connectTypeHierarchyWithoutMembers(); return false; } // Answer whether a cycle was found between the sourceType & the superType private boolean detectHierarchyCycle(SourceTypeBinding sourceType, ReferenceBinding superType, TypeReference reference) { if (superType.isRawType()) superType = ((RawTypeBinding) superType).genericType(); // by this point the superType must be a binary or source type if (TypeBinding.equalsEquals(sourceType, superType)) { problemReporter().hierarchyCircularity(sourceType, superType, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; return true; } if (superType.isMemberType()) { ReferenceBinding current = superType.enclosingType(); do { if (current.isHierarchyBeingActivelyConnected()) { problemReporter().hierarchyCircularity(sourceType, current, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; current.tagBits |= TagBits.HierarchyHasProblems; return true; } } while ((current = current.enclosingType()) != null); } if (superType.isBinaryBinding()) { // force its superclass & superinterfaces to be found... 2 possibilities exist - the source type is included in the hierarchy of: // - a binary type... this case MUST be caught & reported here // - another source type... this case is reported against the other source type if (superType.problemId() != ProblemReasons.NotFound && (superType.tagBits & TagBits.HierarchyHasProblems) != 0) { sourceType.tagBits |= TagBits.HierarchyHasProblems; problemReporter().hierarchyHasProblems(sourceType); return true; } boolean hasCycle = false; ReferenceBinding parentType = superType.superclass(); if (parentType != null) { if (TypeBinding.equalsEquals(sourceType, parentType)) { problemReporter().hierarchyCircularity(sourceType, superType, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; superType.tagBits |= TagBits.HierarchyHasProblems; return true; } if (parentType.isParameterizedType()) parentType = ((ParameterizedTypeBinding) parentType).genericType(); hasCycle |= detectHierarchyCycle(sourceType, parentType, reference); if ((parentType.tagBits & TagBits.HierarchyHasProblems) != 0) { sourceType.tagBits |= TagBits.HierarchyHasProblems; parentType.tagBits |= TagBits.HierarchyHasProblems; // propagate down the hierarchy } } ReferenceBinding[] itsInterfaces = superType.superInterfaces(); if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { for (int i = 0, length = itsInterfaces.length; i < length; i++) { ReferenceBinding anInterface = itsInterfaces[i]; if (TypeBinding.equalsEquals(sourceType, anInterface)) { problemReporter().hierarchyCircularity(sourceType, superType, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; superType.tagBits |= TagBits.HierarchyHasProblems; return true; } if (anInterface.isParameterizedType()) anInterface = ((ParameterizedTypeBinding) anInterface).genericType(); hasCycle |= detectHierarchyCycle(sourceType, anInterface, reference); if ((anInterface.tagBits & TagBits.HierarchyHasProblems) != 0) { sourceType.tagBits |= TagBits.HierarchyHasProblems; superType.tagBits |= TagBits.HierarchyHasProblems; } } } return hasCycle; } if (superType.isHierarchyBeingActivelyConnected()) { org.eclipse.jdt.internal.compiler.ast.TypeReference ref = ((SourceTypeBinding) superType).scope.superTypeReference; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=133071 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=121734 if (ref != null && ref.resolvedType != null) { ReferenceBinding s = (ReferenceBinding) ref.resolvedType; do { if (s.isHierarchyBeingActivelyConnected()) { problemReporter().hierarchyCircularity(sourceType, superType, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; superType.tagBits |= TagBits.HierarchyHasProblems; return true; } } while ((s = s.enclosingType()) != null); } if (ref != null && ref.resolvedType == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=319885 Don't cry foul prematurely. // Check the edges traversed to see if there really is a cycle. char [] referredName = ref.getLastToken(); for (Iterator iter = environment().typesBeingConnected.iterator(); iter.hasNext();) { SourceTypeBinding type = (SourceTypeBinding) iter.next(); if (CharOperation.equals(referredName, type.sourceName())) { problemReporter().hierarchyCircularity(sourceType, superType, reference); sourceType.tagBits |= TagBits.HierarchyHasProblems; superType.tagBits |= TagBits.HierarchyHasProblems; return true; } } } } if ((superType.tagBits & TagBits.BeginHierarchyCheck) == 0) { // ensure if this is a source superclass that it has already been checked if (superType.isValidBinding() && !superType.isUnresolvedType()) ((SourceTypeBinding) superType).scope.connectTypeHierarchyWithoutMembers(); } if ((superType.tagBits & TagBits.HierarchyHasProblems) != 0) sourceType.tagBits |= TagBits.HierarchyHasProblems; return false; } private ReferenceBinding findSupertype(TypeReference typeReference) { CompilationUnitScope unitScope = compilationUnitScope(); LookupEnvironment env = unitScope.environment; try { env.missingClassFileLocation = typeReference; typeReference.aboutToResolve(this); // allows us to trap completion & selection nodes unitScope.recordQualifiedReference(typeReference.getTypeName()); this.superTypeReference = typeReference; ReferenceBinding superType = (ReferenceBinding) typeReference.resolveSuperType(this); return superType; } catch (AbortCompilation e) { SourceTypeBinding sourceType = this.referenceContext.binding; if (sourceType.superInterfaces == null) sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); // be more resilient for hierarchies (144976) e.updateContext(typeReference, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; this.superTypeReference = null; } } /* Answer the problem reporter to use for raising new problems. * * Note that as a side-effect, this updates the current reference context * (unit, type or method) in case the problem handler decides it is necessary * to abort. */ @Override public ProblemReporter problemReporter() { MethodScope outerMethodScope; if ((outerMethodScope = outerMostMethodScope()) == null) { ProblemReporter problemReporter = referenceCompilationUnit().problemReporter; problemReporter.referenceContext = this.referenceContext; return problemReporter; } return outerMethodScope.problemReporter(); } /* Answer the reference type of this scope. * It is the nearest enclosing type of this scope. */ public TypeDeclaration referenceType() { return this.referenceContext; } @Override public boolean hasDefaultNullnessFor(int location, int sourceStart) { int nonNullByDefaultValue = localNonNullByDefaultValue(sourceStart); if (nonNullByDefaultValue != 0) { return (nonNullByDefaultValue & location) != 0; } SourceTypeBinding binding = this.referenceContext.binding; if (binding != null) { int nullDefault = binding.getNullDefault(); if (nullDefault != 0) { return (nullDefault & location) != 0; } } return this.parent.hasDefaultNullnessFor(location, sourceStart); } @Override public /* @Nullable */ Binding checkRedundantDefaultNullness(int nullBits, int sourceStart) { Binding target = localCheckRedundantDefaultNullness(nullBits, sourceStart); if (target != null) { return target; } SourceTypeBinding binding = this.referenceContext.binding; if (binding != null) { int nullDefault = binding.getNullDefault(); if (nullDefault != 0) { return (nullDefault == nullBits) ? binding : null; } } return this.parent.checkRedundantDefaultNullness(nullBits, sourceStart); } @Override public String toString() { if (this.referenceContext != null) return "--- Class Scope ---\n\n" //$NON-NLS-1$ + this.referenceContext.binding.toString(); return "--- Class Scope ---\n\n Binding not initialized" ; //$NON-NLS-1$ } }