--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package net.sourceforge.phpdt.internal.compiler.lookup;
+
+import net.sourceforge.phpdt.core.compiler.CharOperation;
+import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
+import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
+import net.sourceforge.phpdt.internal.compiler.util.HashtableOfObject;
+import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.Clinit;
+import net.sourceforge.phpeclipse.internal.compiler.ast.FieldDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.TypeReference;
+
+public class ClassScope extends Scope {
+ public TypeDeclaration referenceContext;
+
+ public ClassScope(Scope parent, TypeDeclaration context) {
+ super(CLASS_SCOPE, parent);
+ this.referenceContext = context;
+ }
+
+ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding supertype) {
+
+ LocalTypeBinding anonymousType = buildLocalType(enclosingType, enclosingType.fPackage);
+
+ SourceTypeBinding sourceType = referenceContext.binding;
+ if (supertype.isInterface()) {
+ sourceType.superclass = getJavaLangObject();
+ sourceType.superInterfaces = new ReferenceBinding[] { supertype };
+ } else {
+ sourceType.superclass = supertype;
+ sourceType.superInterfaces = TypeBinding.NoSuperInterfaces;
+ }
+ connectMemberTypes();
+ buildFieldsAndMethods();
+ anonymousType.faultInTypesForFieldsAndMethods();
+ sourceType.verifyMethods(environment().methodVerifier());
+ }
+
+ private void buildFields() {
+ if (referenceContext.fields == null) {
+ referenceContext.binding.fields = NoFields;
+ return;
+ }
+ // count the number of fields vs. initializers
+ FieldDeclaration[] fields = referenceContext.fields;
+ int size = fields.length;
+ int count = 0;
+ for (int i = 0; i < size; i++)
+ if (fields[i].isField())
+ count++;
+
+ // iterate the field declarations to create the bindings, lose all duplicates
+ FieldBinding[] fieldBindings = new FieldBinding[count];
+ HashtableOfObject knownFieldNames = new HashtableOfObject(count);
+ boolean duplicate = false;
+ count = 0;
+ for (int i = 0; i < size; i++) {
+ FieldDeclaration field = fields[i];
+ if (!field.isField()) {
+ if (referenceContext.binding.isInterface())
+ problemReporter().interfaceCannotHaveInitializers(referenceContext.binding, field);
+ } else {
+ FieldBinding fieldBinding = new FieldBinding(field, null, referenceContext.binding);
+ // field's type will be resolved when needed for top level types
+ checkAndSetModifiersForField(fieldBinding, field);
+
+ if (knownFieldNames.containsKey(field.name)) {
+ duplicate = true;
+ 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(referenceContext.binding, previousField);
+ previousField.binding = null;
+ break;
+ }
+ }
+ }
+ knownFieldNames.put(field.name, null); // ensure that the duplicate field is found & removed
+ problemReporter().duplicateFieldInType(referenceContext.binding, field);
+ field.binding = null;
+ } else {
+ knownFieldNames.put(field.name, fieldBinding);
+ // remember that we have seen a field with this name
+ if (fieldBinding != null)
+ fieldBindings[count++] = fieldBinding;
+ }
+ }
+ }
+ // remove duplicate fields
+ if (duplicate) {
+ FieldBinding[] newFieldBindings = new FieldBinding[knownFieldNames.size() - 1];
+ // we know we'll be removing at least 1 duplicate name
+ size = count;
+ count = 0;
+ for (int i = 0; i < size; i++) {
+ FieldBinding fieldBinding = fieldBindings[i];
+ if (knownFieldNames.get(fieldBinding.name) != null)
+ newFieldBindings[count++] = fieldBinding;
+ }
+ fieldBindings = newFieldBindings;
+ }
+
+ if (count != fieldBindings.length)
+ System.arraycopy(fieldBindings, 0, fieldBindings = new FieldBinding[count], 0, count);
+ for (int i = 0; i < count; i++)
+ fieldBindings[i].id = i;
+ referenceContext.binding.fields = fieldBindings;
+ }
+
+ void buildFieldsAndMethods() {
+ buildFields();
+ buildMethods();
+
+ SourceTypeBinding sourceType = referenceContext.binding;
+ 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) {
+ referenceContext.scope = this;
+ referenceContext.staticInitializerScope = new MethodScope(this, referenceContext, true);
+ referenceContext.initializerScope = new MethodScope(this, referenceContext, false);
+
+ // build the binding or the local type
+ LocalTypeBinding localType = new LocalTypeBinding(this, enclosingType);
+ referenceContext.binding = localType;
+ checkAndSetModifiers();
+
+ // Look at member types
+ ReferenceBinding[] memberTypeBindings = NoMemberTypes;
+ if (referenceContext.memberTypes != null) {
+ int size = referenceContext.memberTypes.length;
+ memberTypeBindings = new ReferenceBinding[size];
+ int count = 0;
+ nextMember : for (int i = 0; i < size; i++) {
+ TypeDeclaration memberContext = referenceContext.memberTypes[i];
+ if (memberContext.isInterface()) {
+ problemReporter().nestedClassCannotDeclareInterface(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().hidingEnclosingType(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(referenceContext.memberTypes[j].name, memberContext.name)) {
+ problemReporter().duplicateNestedType(memberContext);
+ continue nextMember;
+ }
+ }
+
+ ClassScope memberScope = new ClassScope(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.memberTypes = memberTypeBindings;
+ return localType;
+ }
+
+ void buildLocalTypeBinding(SourceTypeBinding enclosingType) {
+
+ LocalTypeBinding localType = buildLocalType(enclosingType, enclosingType.fPackage);
+ connectTypeHierarchy();
+ buildFieldsAndMethods();
+ localType.faultInTypesForFieldsAndMethods();
+
+ referenceContext.binding.verifyMethods(environment().methodVerifier());
+ }
+
+ private void buildMethods() {
+ if (referenceContext.methods == null) {
+ referenceContext.binding.methods = NoMethods;
+ return;
+ }
+
+ // iterate the method declarations to create the bindings
+ AbstractMethodDeclaration[] methods = referenceContext.methods;
+ int size = methods.length;
+ int clinitIndex = -1;
+ for (int i = 0; i < size; i++) {
+ if (methods[i] instanceof Clinit) {
+ clinitIndex = i;
+ break;
+ }
+ }
+ MethodBinding[] methodBindings = new MethodBinding[clinitIndex == -1 ? size : size - 1];
+
+ int count = 0;
+ 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;
+ }
+ }
+ if (count != methodBindings.length)
+ System.arraycopy(methodBindings, 0, methodBindings = new MethodBinding[count], 0, count);
+
+ referenceContext.binding.methods = methodBindings;
+ referenceContext.binding.modifiers |= AccUnresolved; // until methods() is sent
+ }
+ SourceTypeBinding buildType(SourceTypeBinding enclosingType, PackageBinding packageBinding) {
+ // provide the typeDeclaration with needed scopes
+ referenceContext.scope = this;
+ referenceContext.staticInitializerScope = new MethodScope(this, referenceContext, true);
+ referenceContext.initializerScope = new MethodScope(this, referenceContext, false);
+
+ if (enclosingType == null) {
+ char[][] className = CharOperation.arrayConcat(packageBinding.compoundName, referenceContext.name);
+ referenceContext.binding = new SourceTypeBinding(className, packageBinding, this);
+ } else {
+ char[][] className = CharOperation.deepCopy(enclosingType.compoundName);
+ className[className.length - 1] =
+ CharOperation.concat(className[className.length - 1], referenceContext.name, '$');
+ referenceContext.binding = new MemberTypeBinding(className, this, enclosingType);
+ }
+
+ SourceTypeBinding sourceType = referenceContext.binding;
+ sourceType.fPackage.addType(sourceType);
+ checkAndSetModifiers();
+
+ // Look at member types
+ ReferenceBinding[] memberTypeBindings = NoMemberTypes;
+ if (referenceContext.memberTypes != null) {
+ int size = referenceContext.memberTypes.length;
+ memberTypeBindings = new ReferenceBinding[size];
+ int count = 0;
+ nextMember : for (int i = 0; i < size; i++) {
+ TypeDeclaration memberContext = referenceContext.memberTypes[i];
+ if (memberContext.isInterface()
+ && sourceType.isNestedType()
+ && sourceType.isClass()
+ && !sourceType.isStatic()) {
+ problemReporter().nestedClassCannotDeclareInterface(memberContext);
+ continue nextMember;
+ }
+ ReferenceBinding type = sourceType;
+ // check that the member does not conflict with an enclosing type
+ do {
+ if (CharOperation.equals(type.sourceName, memberContext.name)) {
+ problemReporter().hidingEnclosingType(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(referenceContext.memberTypes[j].name, memberContext.name)) {
+ problemReporter().duplicateNestedType(memberContext);
+ continue nextMember;
+ }
+ }
+
+ ClassScope memberScope = new ClassScope(this, memberContext);
+ memberTypeBindings[count++] = memberScope.buildType(sourceType, packageBinding);
+ }
+ if (count != size)
+ System.arraycopy(memberTypeBindings, 0, memberTypeBindings = new ReferenceBinding[count], 0, count);
+ }
+ sourceType.memberTypes = memberTypeBindings;
+ return sourceType;
+ }
+
+ private void checkAndSetModifiers() {
+ SourceTypeBinding sourceType = referenceContext.binding;
+ int modifiers = sourceType.modifiers;
+ if ((modifiers & AccAlternateModifierProblem) != 0)
+ problemReporter().duplicateModifierForType(sourceType);
+
+ ReferenceBinding enclosingType = sourceType.enclosingType();
+ boolean isMemberType = sourceType.isMemberType();
+
+ if (isMemberType) {
+ // checks for member types before local types to catch local members
+// if (enclosingType.isStrictfp())
+// modifiers |= AccStrictfp;
+ if (enclosingType.isDeprecated())
+ modifiers |= AccDeprecatedImplicitly;
+ if (enclosingType.isInterface())
+ modifiers |= AccPublic;
+ } else if (sourceType.isLocalType()) {
+ if (sourceType.isAnonymousType())
+ modifiers |= AccFinal;
+ ReferenceContext refContext = methodScope().referenceContext;
+ if (refContext instanceof TypeDeclaration) {
+ ReferenceBinding type = ((TypeDeclaration) refContext).binding;
+// if (type.isStrictfp())
+// modifiers |= AccStrictfp;
+ if (type.isDeprecated())
+ modifiers |= AccDeprecatedImplicitly;
+ } else {
+ MethodBinding method = ((AbstractMethodDeclaration) refContext).binding;
+ if (method != null){
+// if (method.isStrictfp())
+// modifiers |= AccStrictfp;
+ if (method.isDeprecated())
+ modifiers |= AccDeprecatedImplicitly;
+ }
+ }
+ }
+ // after this point, tests on the 16 bits reserved.
+ int realModifiers = modifiers & AccJustFlag;
+
+ if ((realModifiers & AccInterface) != 0) {
+ // detect abnormal cases for interfaces
+ if (isMemberType) {
+ int unexpectedModifiers =
+ ~(AccPublic | AccPrivate | AccProtected | AccStatic | AccAbstract | AccInterface );//| AccStrictfp);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ 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 {
+ int unexpectedModifiers = ~(AccPublic | AccAbstract | AccInterface);// | AccStrictfp);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ problemReporter().illegalModifierForInterface(sourceType);
+ }
+ modifiers |= AccAbstract;
+ } else {
+ // detect abnormal cases for types
+ if (isMemberType) { // includes member types defined inside local types
+ int unexpectedModifiers =
+ ~(AccPublic | AccPrivate | AccProtected | AccStatic | AccAbstract | AccFinal);// | AccStrictfp);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ problemReporter().illegalModifierForMemberClass(sourceType);
+ } else if (sourceType.isLocalType()) {
+ int unexpectedModifiers = ~(AccAbstract | AccFinal);// | AccStrictfp);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ problemReporter().illegalModifierForLocalClass(sourceType);
+ } else {
+ int unexpectedModifiers = ~(AccPublic | AccAbstract | AccFinal);// | AccStrictfp);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ problemReporter().illegalModifierForClass(sourceType);
+ }
+
+ // check that Final and Abstract are not set together
+ if ((realModifiers & (AccFinal | AccAbstract)) == (AccFinal | AccAbstract))
+ problemReporter().illegalModifierCombinationFinalAbstractForClass(sourceType);
+ }
+
+ if (isMemberType) {
+ // test visibility modifiers inconsistency, isolate the accessors bits
+ if (enclosingType.isInterface()) {
+ if ((realModifiers & (AccProtected | AccPrivate)) != 0) {
+ problemReporter().illegalVisibilityModifierForInterfaceMemberType(sourceType);
+
+ // need to keep the less restrictive
+ if ((realModifiers & AccProtected) != 0)
+ modifiers ^= AccProtected;
+ if ((realModifiers & AccPrivate) != 0)
+ modifiers ^= AccPrivate;
+ }
+ } else {
+ int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
+ if ((accessorBits & (accessorBits - 1)) > 1) {
+ problemReporter().illegalVisibilityModifierCombinationForMemberType(sourceType);
+
+ // need to keep the less restrictive
+ if ((accessorBits & AccPublic) != 0) {
+ if ((accessorBits & AccProtected) != 0)
+ modifiers ^= AccProtected;
+ if ((accessorBits & AccPrivate) != 0)
+ modifiers ^= AccPrivate;
+ }
+ if ((accessorBits & AccProtected) != 0)
+ if ((accessorBits & AccPrivate) != 0)
+ modifiers ^= AccPrivate;
+ }
+ }
+
+ // static modifier test
+ if ((realModifiers & AccStatic) == 0) {
+ if (enclosingType.isInterface())
+ modifiers |= 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;
+ if ((modifiers & AccAlternateModifierProblem) != 0)
+ problemReporter().duplicateModifierForField(fieldBinding.declaringClass, fieldDecl);
+
+ if (fieldBinding.declaringClass.isInterface()) {
+ int expectedValue = AccPublic | AccStatic | AccFinal;
+ // set the modifiers
+ modifiers |= expectedValue;
+
+ // and then check that they are the only ones
+ if ((modifiers & AccJustFlag) != expectedValue)
+ problemReporter().illegalModifierForInterfaceField(fieldBinding.declaringClass, fieldDecl);
+ fieldBinding.modifiers = modifiers;
+ return;
+ }
+
+ // after this point, tests on the 16 bits reserved.
+ int realModifiers = modifiers & AccJustFlag;
+ int unexpectedModifiers =
+ ~(AccPublic | AccPrivate | AccProtected | AccFinal | AccStatic);// | AccTransient | AccVolatile);
+ if ((realModifiers & unexpectedModifiers) != 0)
+ problemReporter().illegalModifierForField(fieldBinding.declaringClass, fieldDecl);
+
+ int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
+ if ((accessorBits & (accessorBits - 1)) > 1) {
+ problemReporter().illegalVisibilityModifierCombinationForField(
+ fieldBinding.declaringClass,
+ fieldDecl);
+
+ // need to keep the less restrictive
+ if ((accessorBits & AccPublic) != 0) {
+ if ((accessorBits & AccProtected) != 0)
+ modifiers ^= AccProtected;
+ if ((accessorBits & AccPrivate) != 0)
+ modifiers ^= AccPrivate;
+ }
+ if ((accessorBits & AccProtected) != 0)
+ if ((accessorBits & AccPrivate) != 0)
+ modifiers ^= AccPrivate;
+ }
+
+// if ((realModifiers & (AccFinal | AccVolatile)) == (AccFinal | AccVolatile))
+// problemReporter().illegalModifierCombinationFinalVolatileForField(
+// fieldBinding.declaringClass,
+// fieldDecl);
+
+ if (fieldDecl.initialization == null && (modifiers & AccFinal) != 0) {
+ modifiers |= AccBlankFinal;
+ }
+ fieldBinding.modifiers = modifiers;
+ }
+
+ 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
+ ReferenceBinding currentType = sourceType;
+ ReferenceBinding[][] interfacesToVisit = null;
+ int lastPosition = -1;
+ do {
+ if ((currentType.tagBits & HasNoMemberTypes) != 0)
+ break; // already know it has no inherited member types, can stop looking up
+ if (currentType.memberTypes() != NoMemberTypes)
+ return; // has member types
+ ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
+ if (itsInterfaces != NoSuperInterfaces) {
+ if (interfacesToVisit == null)
+ interfacesToVisit = new ReferenceBinding[5][];
+ if (++lastPosition == interfacesToVisit.length)
+ System.arraycopy(
+ interfacesToVisit,
+ 0,
+ interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
+ 0,
+ lastPosition);
+ interfacesToVisit[lastPosition] = itsInterfaces;
+ }
+ } while ((currentType = currentType.superclass()) != null);
+
+ boolean hasMembers = false;
+ if (interfacesToVisit != null) {
+ done : for (int i = 0; i <= lastPosition; i++) {
+ ReferenceBinding[] interfaces = interfacesToVisit[i];
+ for (int j = 0, length = interfaces.length; j < length; j++) {
+ ReferenceBinding anInterface = interfaces[j];
+ if ((anInterface.tagBits & InterfaceVisited) == 0) { // if interface as not already been visited
+ anInterface.tagBits |= InterfaceVisited;
+ if ((anInterface.tagBits & HasNoMemberTypes) != 0)
+ continue; // already know it has no inherited member types
+ if (anInterface.memberTypes() != NoMemberTypes) {
+ hasMembers = true;
+ break done;
+ }
+
+ ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
+ if (itsInterfaces != NoSuperInterfaces) {
+ if (++lastPosition == interfacesToVisit.length)
+ System.arraycopy(
+ interfacesToVisit,
+ 0,
+ interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
+ 0,
+ lastPosition);
+ interfacesToVisit[lastPosition] = itsInterfaces;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i <= lastPosition; i++) {
+ ReferenceBinding[] interfaces = interfacesToVisit[i];
+ for (int j = 0, length = interfaces.length; j < length; j++) {
+ interfaces[j].tagBits &= ~InterfaceVisited;
+ if (!hasMembers)
+ interfaces[j].tagBits |= HasNoMemberTypes;
+ }
+ }
+ }
+
+ if (!hasMembers) {
+ currentType = sourceType;
+ do {
+ currentType.tagBits |= HasNoMemberTypes;
+ } while ((currentType = currentType.superclass()) != null);
+ }
+ }
+
+ private void connectMemberTypes() {
+ SourceTypeBinding sourceType = referenceContext.binding;
+ if (sourceType.memberTypes != NoMemberTypes)
+ for (int i = 0, size = sourceType.memberTypes.length; i < size; i++)
+ ((SourceTypeBinding) sourceType.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 = referenceContext.binding;
+ if (isJavaLangObject(sourceType)) { // handle the case of redefining java.lang.Object up front
+ sourceType.superclass = null;
+ sourceType.superInterfaces = NoSuperInterfaces;
+ if (referenceContext.superclass != null || referenceContext.superInterfaces != null)
+ problemReporter().objectCannotHaveSuperTypes(sourceType);
+ return true; // do not propagate Object's hierarchy problems down to every subtype
+ }
+ if (referenceContext.superclass == null) {
+ sourceType.superclass = getJavaLangObject();
+ return !detectCycle(sourceType, sourceType.superclass, null);
+ }
+ ReferenceBinding superclass = findSupertype(referenceContext.superclass);
+ if (superclass != null) { // is null if a cycle was detected cycle
+ if (!superclass.isValidBinding()) {
+ problemReporter().invalidSuperclass(sourceType, referenceContext.superclass, superclass);
+ } else if (superclass.isInterface()) {
+ problemReporter().superclassMustBeAClass(sourceType, referenceContext.superclass, superclass);
+ } else if (superclass.isFinal()) {
+ problemReporter().classExtendFinalClass(sourceType, referenceContext.superclass, superclass);
+ } else {
+ // only want to reach here when no errors are reported
+ referenceContext.superclass.resolvedType = superclass;
+ sourceType.superclass = superclass;
+ return true;
+ }
+ }
+ sourceType.tagBits |= HierarchyHasProblems;
+ sourceType.superclass = getJavaLangObject();
+ if ((sourceType.superclass.tagBits & BeginHierarchyCheck) == 0)
+ detectCycle(sourceType, sourceType.superclass, null);
+ return false; // reported some error against the source type
+ }
+
+ /*
+ 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 = referenceContext.binding;
+ sourceType.superInterfaces = NoSuperInterfaces;
+ if (referenceContext.superInterfaces == null)
+ return true;
+ if (isJavaLangObject(sourceType)) // already handled the case of redefining java.lang.Object
+ return true;
+
+ boolean noProblems = true;
+ int length = referenceContext.superInterfaces.length;
+ ReferenceBinding[] interfaceBindings = new ReferenceBinding[length];
+ int count = 0;
+ nextInterface : for (int i = 0; i < length; i++) {
+ ReferenceBinding superInterface = findSupertype(referenceContext.superInterfaces[i]);
+ if (superInterface == null) { // detected cycle
+ noProblems = false;
+ continue nextInterface;
+ }
+ if (!superInterface.isValidBinding()) {
+ problemReporter().invalidSuperinterface(
+ sourceType,
+ referenceContext.superInterfaces[i],
+ superInterface);
+ sourceType.tagBits |= HierarchyHasProblems;
+ noProblems = false;
+ continue nextInterface;
+ }
+ // Check for a duplicate interface once the name is resolved, otherwise we may be confused (ie : a.b.I and c.d.I)
+ for (int k = 0; k < count; k++) {
+ if (interfaceBindings[k] == superInterface) {
+ // should this be treated as a warning?
+ problemReporter().duplicateSuperinterface(sourceType, referenceContext, superInterface);
+ continue nextInterface;
+ }
+ }
+ if (superInterface.isClass()) {
+ problemReporter().superinterfaceMustBeAnInterface(sourceType, referenceContext, superInterface);
+ sourceType.tagBits |= HierarchyHasProblems;
+ noProblems = false;
+ continue nextInterface;
+ }
+
+ referenceContext.superInterfaces[i].resolvedType = superInterface;
+ // only want to reach here when no errors are reported
+ 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.superInterfaces = interfaceBindings;
+ }
+ return noProblems;
+ }
+
+ void connectTypeHierarchy() {
+ SourceTypeBinding sourceType = referenceContext.binding;
+ if ((sourceType.tagBits & BeginHierarchyCheck) == 0) {
+ boolean noProblems = true;
+ sourceType.tagBits |= BeginHierarchyCheck;
+ if (sourceType.isClass())
+ noProblems &= connectSuperclass();
+ noProblems &= connectSuperInterfaces();
+ sourceType.tagBits |= EndHierarchyCheck;
+ if (noProblems && sourceType.isHierarchyInconsistent())
+ problemReporter().hierarchyHasProblems(sourceType);
+ }
+ connectMemberTypes();
+ checkForInheritedMemberTypes(sourceType);
+ }
+
+ private void connectTypeHierarchyWithoutMembers() {
+ // must ensure the imports are resolved
+ if (parent instanceof CompilationUnitScope) {
+ if (((CompilationUnitScope) parent).imports == null)
+ ((CompilationUnitScope) parent).checkAndSetImports();
+ } else if (parent instanceof ClassScope) {
+ // ensure that the enclosing type has already been checked
+ ((ClassScope) parent).connectTypeHierarchyWithoutMembers();
+ }
+
+ // double check that the hierarchy search has not already begun...
+ SourceTypeBinding sourceType = referenceContext.binding;
+ if ((sourceType.tagBits & BeginHierarchyCheck) != 0)
+ return;
+
+ boolean noProblems = true;
+ sourceType.tagBits |= BeginHierarchyCheck;
+ if (sourceType.isClass())
+ noProblems &= connectSuperclass();
+ noProblems &= connectSuperInterfaces();
+ sourceType.tagBits |= EndHierarchyCheck;
+ if (noProblems && sourceType.isHierarchyInconsistent())
+ problemReporter().hierarchyHasProblems(sourceType);
+ }
+
+ // Answer whether a cycle was found between the sourceType & the superType
+ private boolean detectCycle(
+ SourceTypeBinding sourceType,
+ ReferenceBinding superType,
+ TypeReference reference) {
+ if (sourceType == superType) {
+ problemReporter().hierarchyCircularity(sourceType, superType, reference);
+ sourceType.tagBits |= HierarchyHasProblems;
+ return true;
+ }
+
+ 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
+ boolean hasCycle = false;
+ if (superType.superclass() != null) {
+ if (sourceType == superType.superclass()) {
+ problemReporter().hierarchyCircularity(sourceType, superType, reference);
+ sourceType.tagBits |= HierarchyHasProblems;
+ superType.tagBits |= HierarchyHasProblems;
+ return true;
+ }
+ hasCycle |= detectCycle(sourceType, superType.superclass(), reference);
+ if ((superType.superclass().tagBits & HierarchyHasProblems) != 0) {
+ sourceType.tagBits |= HierarchyHasProblems;
+ superType.tagBits |= HierarchyHasProblems; // propagate down the hierarchy
+ }
+ }
+
+ ReferenceBinding[] itsInterfaces = superType.superInterfaces();
+ if (itsInterfaces != NoSuperInterfaces) {
+ for (int i = 0, length = itsInterfaces.length; i < length; i++) {
+ ReferenceBinding anInterface = itsInterfaces[i];
+ if (sourceType == anInterface) {
+ problemReporter().hierarchyCircularity(sourceType, superType, reference);
+ sourceType.tagBits |= HierarchyHasProblems;
+ superType.tagBits |= HierarchyHasProblems;
+ return true;
+ }
+ hasCycle |= detectCycle(sourceType, anInterface, reference);
+ if ((anInterface.tagBits & HierarchyHasProblems) != 0) {
+ sourceType.tagBits |= HierarchyHasProblems;
+ superType.tagBits |= HierarchyHasProblems;
+ }
+ }
+ }
+ return hasCycle;
+ }
+
+ if ((superType.tagBits & EndHierarchyCheck) == 0
+ && (superType.tagBits & BeginHierarchyCheck) != 0) {
+ problemReporter().hierarchyCircularity(sourceType, superType, reference);
+ sourceType.tagBits |= HierarchyHasProblems;
+ superType.tagBits |= HierarchyHasProblems;
+ return true;
+ }
+ if ((superType.tagBits & BeginHierarchyCheck) == 0)
+ // ensure if this is a source superclass that it has already been checked
+ ((SourceTypeBinding) superType).scope.connectTypeHierarchyWithoutMembers();
+ if ((superType.tagBits & HierarchyHasProblems) != 0)
+ sourceType.tagBits |= HierarchyHasProblems;
+ return false;
+ }
+
+ private ReferenceBinding findSupertype(TypeReference typeReference) {
+ typeReference.aboutToResolve(this); // allows us to trap completion & selection nodes
+ char[][] compoundName = typeReference.getTypeName();
+ compilationUnitScope().recordQualifiedReference(compoundName);
+ SourceTypeBinding sourceType = referenceContext.binding;
+ int size = compoundName.length;
+ int n = 1;
+ ReferenceBinding superType;
+
+ // resolve the first name of the compoundName
+ if (CharOperation.equals(compoundName[0], sourceType.sourceName)) {
+ superType = sourceType;
+ // match against the sourceType even though nested members cannot be supertypes
+ } else {
+ Binding typeOrPackage = parent.getTypeOrPackage(compoundName[0], TYPE | PACKAGE);
+ if (typeOrPackage == null || !typeOrPackage.isValidBinding())
+ return new ProblemReferenceBinding(
+ compoundName[0],
+ typeOrPackage == null ? NotFound : typeOrPackage.problemId());
+
+ boolean checkVisibility = false;
+ for (; n < size; n++) {
+ if (!(typeOrPackage instanceof PackageBinding))
+ break;
+ PackageBinding packageBinding = (PackageBinding) typeOrPackage;
+ typeOrPackage = packageBinding.getTypeOrPackage(compoundName[n]);
+ if (typeOrPackage == null || !typeOrPackage.isValidBinding())
+ return new ProblemReferenceBinding(
+ CharOperation.subarray(compoundName, 0, n + 1),
+ typeOrPackage == null ? NotFound : typeOrPackage.problemId());
+ checkVisibility = true;
+ }
+
+ // convert to a ReferenceBinding
+ if (typeOrPackage instanceof PackageBinding) // error, the compoundName is a packageName
+ return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, n), NotFound);
+ superType = (ReferenceBinding) typeOrPackage;
+ compilationUnitScope().recordTypeReference(superType); // to record supertypes
+
+ if (checkVisibility
+ && n == size) { // if we're finished and know the final supertype then check visibility
+ if (!superType.canBeSeenBy(sourceType.fPackage))
+ // its a toplevel type so just check package access
+ return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, n), superType, NotVisible);
+ }
+ }
+ // at this point we know we have a type but we have to look for cycles
+ while (true) {
+ // must detect cycles & force connection up the hierarchy... also handle cycles with binary types.
+ // must be guaranteed that the superType knows its entire hierarchy
+ if (detectCycle(sourceType, superType, typeReference))
+ return null; // cycle error was already reported
+
+ if (n >= size)
+ break;
+
+ // retrieve the next member type
+ char[] typeName = compoundName[n++];
+ superType = findMemberType(typeName, superType);
+ if (superType == null)
+ return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, n), NotFound);
+ if (!superType.isValidBinding()) {
+ superType.compoundName = CharOperation.subarray(compoundName, 0, n);
+ return superType;
+ }
+ }
+ return superType;
+ }
+
+ /* 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.
+ */
+ public ProblemReporter problemReporter() {
+ MethodScope outerMethodScope;
+ if ((outerMethodScope = outerMostMethodScope()) == null) {
+ ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
+ problemReporter.referenceContext = referenceContext;
+ return problemReporter;
+ } else {
+ return outerMethodScope.problemReporter();
+ }
+ }
+
+ /* Answer the reference type of this scope.
+ * It is the nearest enclosing type of this scope.
+ */
+ public TypeDeclaration referenceType() {
+ return referenceContext;
+ }
+
+ public String toString() {
+ if (referenceContext != null)
+ return "--- Class Scope ---\n\n" //$NON-NLS-1$
+ +referenceContext.binding.toString();
+ else
+ return "--- Class Scope ---\n\n Binding not initialized" ; //$NON-NLS-1$
+ }
+}