/**
   * Casting an enclosing instance will considered as useful if removing it would actually bind to a
   * different type
   */
  public static void checkNeedForEnclosingInstanceCast(
      BlockScope scope,
      Expression enclosingInstance,
      TypeBinding enclosingInstanceType,
      TypeBinding memberType) {
    if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck)
        == ProblemSeverities.Ignore) return;

    TypeBinding castedExpressionType = ((CastExpression) enclosingInstance).expression.resolvedType;
    if (castedExpressionType == null) return; // cannot do better
    // obvious identity cast
    if (TypeBinding.equalsEquals(castedExpressionType, enclosingInstanceType)) {
      scope.problemReporter().unnecessaryCast((CastExpression) enclosingInstance);
    } else if (castedExpressionType == TypeBinding.NULL) {
      return; // tolerate null enclosing instance cast
    } else {
      TypeBinding alternateEnclosingInstanceType = castedExpressionType;
      if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType())
        return; // error case
      if (TypeBinding.equalsEquals(
          memberType,
          scope.getMemberType(
              memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType))) {
        scope.problemReporter().unnecessaryCast((CastExpression) enclosingInstance);
      }
    }
  }
 public FlowInfo getInitsForFinalBlankInitializationCheck(
     TypeBinding declaringType, FlowInfo flowInfo) {
   FlowContext current = this;
   FlowInfo inits = flowInfo;
   do {
     if (current instanceof InitializationFlowContext) {
       InitializationFlowContext initializationContext = (InitializationFlowContext) current;
       if (TypeBinding.equalsEquals(
           ((TypeDeclaration) initializationContext.associatedNode).binding, declaringType)) {
         return inits;
       }
       inits = initializationContext.initsBeforeContext;
       current = initializationContext.initializationParent;
     } else if (current instanceof ExceptionHandlingFlowContext) {
       ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current;
       current =
           exceptionContext.initializationParent == null
               ? exceptionContext.parent
               : exceptionContext.initializationParent;
     } else {
       current = current.getLocalParent();
     }
   } while (current != null);
   // not found
   return null;
 }
 public FlowInfo getInitsForFinalBlankInitializationCheck(
     TypeBinding declaringType, FlowInfo flowInfo) {
   FlowContext current = this;
   FlowInfo inits = flowInfo;
   do {
     if (current instanceof InitializationFlowContext) {
       InitializationFlowContext initializationContext = (InitializationFlowContext) current;
       if (TypeBinding.equalsEquals(
           ((TypeDeclaration) initializationContext.associatedNode).binding, declaringType)) {
         return inits;
       }
       inits = initializationContext.initsBeforeContext;
       current = initializationContext.initializationParent;
     } else if (current instanceof ExceptionHandlingFlowContext) {
       if (current instanceof FieldInitsFakingFlowContext) {
         return FlowInfo.DEAD_END; // isDefinitelyAssigned will return true for all fields
       }
       ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current;
       current =
           exceptionContext.initializationParent == null
               ? exceptionContext.parent
               : exceptionContext.initializationParent;
     } else {
       current = current.getLocalParent();
     }
   } while (current != null);
   // not found
   throw new IllegalStateException(declaringType.debugName());
 }
  /**
   * Cast expressions will considered as useful if removing them all would actually bind to a
   * different method (no fine grain analysis on per casted argument basis, simply separate widening
   * cast from narrowing ones)
   */
  public static void checkNeedForArgumentCasts(
      BlockScope scope,
      Expression receiver,
      TypeBinding receiverType,
      MethodBinding binding,
      Expression[] arguments,
      TypeBinding[] argumentTypes,
      final InvocationSite invocationSite) {
    if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck)
        == ProblemSeverities.Ignore) return;

    int length = argumentTypes.length;

    // iterate over arguments, and retrieve original argument types (before cast)
    TypeBinding[] rawArgumentTypes = argumentTypes;
    for (int i = 0; i < length; i++) {
      Expression argument = arguments[i];
      if (argument instanceof CastExpression) {
        // narrowing conversion on base type may change value, thus necessary
        if ((argument.bits & ASTNode.UnnecessaryCast) == 0 && argument.resolvedType.isBaseType()) {
          continue;
        }
        TypeBinding castedExpressionType = ((CastExpression) argument).expression.resolvedType;
        if (castedExpressionType == null) return; // cannot do better
        // obvious identity cast
        if (TypeBinding.equalsEquals(castedExpressionType, argumentTypes[i])) {
          scope.problemReporter().unnecessaryCast((CastExpression) argument);
        } else if (castedExpressionType == TypeBinding.NULL) {
          continue; // tolerate null argument cast
        } else if ((argument.implicitConversion & TypeIds.BOXING) != 0) {
          continue; // boxing has a side effect: (int) char   is not boxed as simple char
        } else {
          if (rawArgumentTypes == argumentTypes) {
            System.arraycopy(
                rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
          }
          // retain original argument type
          rawArgumentTypes[i] = castedExpressionType;
        }
      }
    }
    // perform alternate lookup with original types
    if (rawArgumentTypes != argumentTypes) {
      checkAlternateBinding(
          scope,
          receiver,
          receiverType,
          binding,
          arguments,
          argumentTypes,
          rawArgumentTypes,
          invocationSite);
    }
  }
 private boolean typeUsesTypeVariable(TypeBinding type, TypeBinding variable) {
   if (TypeBinding.equalsEquals(type.leafComponentType(), variable)) return true;
   for (TypeVariableBinding t : type.typeVariables())
     if (typeUsesTypeVariable(t, variable)) return true;
   if (type.isTypeVariable()) {
     if (typeUsesTypeVariable(((ReferenceBinding) type).superclass(), variable)) return true;
     for (TypeBinding superIfc : ((ReferenceBinding) type).superInterfaces())
       if (typeUsesTypeVariable(superIfc, variable)) return true;
   }
   return false;
 }
  private void searchVisibleInterfaceMethods(
      ReferenceBinding[] itsInterfaces,
      ReferenceBinding receiverType,
      Scope scope,
      InvocationSite invocationSite,
      Scope invocationScope,
      boolean onlyStaticMethods,
      ObjectVector methodsFound) {
    if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
      ReferenceBinding[] interfacesToVisit = itsInterfaces;
      int nextPosition = interfacesToVisit.length;

      for (int i = 0; i < nextPosition; i++) {
        ReferenceBinding currentType = interfacesToVisit[i];
        MethodBinding[] methods = currentType.availableMethods();
        if (methods != null) {
          searchVisibleLocalMethods(
              methods,
              receiverType,
              scope,
              invocationSite,
              invocationScope,
              onlyStaticMethods,
              methodsFound);
        }

        itsInterfaces = currentType.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;
          }
        }
      }
    }
  }
  public void checkExceptionHandlers(
      TypeBinding[] raisedExceptions, ASTNode location, FlowInfo flowInfo, BlockScope scope) {
    // check that all the argument exception types are handled
    // JDK Compatible implementation - when an exception type is thrown,
    // all related catch blocks are marked as reachable... instead of those only
    // until the point where it is safely handled (Smarter - see comment at the end)
    int remainingCount; // counting the number of remaining unhandled exceptions
    int raisedCount; // total number of exceptions raised
    if ((raisedExceptions == null) || ((raisedCount = raisedExceptions.length) == 0)) return;
    remainingCount = raisedCount;

    // duplicate the array of raised exceptions since it will be updated
    // (null replaces any handled exception)
    System.arraycopy(
        raisedExceptions, 0, (raisedExceptions = new TypeBinding[raisedCount]), 0, raisedCount);
    FlowContext traversedContext = this;

    ArrayList abruptlyExitedLoops = null;
    while (traversedContext != null) {
      SubRoutineStatement sub;
      if (((sub = traversedContext.subroutine()) != null) && sub.isSubRoutineEscaping()) {
        // traversing a non-returning subroutine means that all unhandled
        // exceptions will actually never get sent...
        return;
      }
      // filter exceptions that are locally caught from the innermost enclosing
      // try statement to the outermost ones.
      if (traversedContext instanceof ExceptionHandlingFlowContext) {
        ExceptionHandlingFlowContext exceptionContext =
            (ExceptionHandlingFlowContext) traversedContext;
        ReferenceBinding[] caughtExceptions;
        if ((caughtExceptions = exceptionContext.handledExceptions) != Binding.NO_EXCEPTIONS) {
          int caughtCount = caughtExceptions.length;
          boolean[] locallyCaught = new boolean[raisedCount]; // at most

          for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
            ReferenceBinding caughtException = caughtExceptions[caughtIndex];
            for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
              TypeBinding raisedException;
              if ((raisedException = raisedExceptions[raisedIndex]) != null) {
                FlowInfo exceptionFlow = flowInfo;
                int state =
                    caughtException == null
                        ? Scope.EQUAL_OR_MORE_SPECIFIC /* any exception */
                        : Scope.compareTypes(raisedException, caughtException);
                if (abruptlyExitedLoops != null
                    && caughtException != null
                    && state != Scope.NOT_RELATED) {
                  for (int i = 0, abruptlyExitedLoopsCount = abruptlyExitedLoops.size();
                      i < abruptlyExitedLoopsCount;
                      i++) {
                    LoopingFlowContext loop = (LoopingFlowContext) abruptlyExitedLoops.get(i);
                    loop.recordCatchContextOfEscapingException(
                        exceptionContext, caughtException, flowInfo);
                  }
                  exceptionFlow =
                      FlowInfo.DEAD_END; // don't use flow info on first round, flow info will be
                  // evaluated during loopback simulation
                }
                switch (state) {
                  case Scope.EQUAL_OR_MORE_SPECIFIC:
                    exceptionContext.recordHandlingException(
                        caughtException,
                        exceptionFlow.unconditionalInits(),
                        raisedException,
                        raisedException, // precise exception that will be caught
                        location,
                        locallyCaught[raisedIndex]);
                    // was already definitely caught ?
                    if (!locallyCaught[raisedIndex]) {
                      locallyCaught[raisedIndex] = true;
                      // remember that this exception has been definitely caught
                      remainingCount--;
                    }
                    break;
                  case Scope.MORE_GENERIC:
                    exceptionContext.recordHandlingException(
                        caughtException,
                        exceptionFlow.unconditionalInits(),
                        raisedException,
                        caughtException,
                        location,
                        false);
                    // was not caught already per construction
                }
              }
            }
          }
          // remove locally caught exceptions from the remaining ones
          for (int i = 0; i < raisedCount; i++) {
            if (locallyCaught[i]) {
              raisedExceptions[i] = null; // removed from the remaining ones.
            }
          }
        }
        // method treatment for unchecked exceptions
        if (exceptionContext.isMethodContext) {
          for (int i = 0; i < raisedCount; i++) {
            TypeBinding raisedException;
            if ((raisedException = raisedExceptions[i]) != null) {
              if (raisedException.isUncheckedException(false)) {
                remainingCount--;
                raisedExceptions[i] = null;
              }
            }
          }
          boolean shouldMergeUnhandledException =
              exceptionContext instanceof ExceptionInferenceFlowContext;
          // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
          // clause will be fixed up later as per JLS 8.6).
          if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration) {
            AbstractMethodDeclaration method =
                (AbstractMethodDeclaration) exceptionContext.associatedNode;
            if (method.isConstructor() && method.binding.declaringClass.isAnonymousType())
              shouldMergeUnhandledException = true;
          }
          if (shouldMergeUnhandledException) {
            for (int i = 0; i < raisedCount; i++) {
              TypeBinding raisedException;
              if ((raisedException = raisedExceptions[i]) != null) {
                exceptionContext.mergeUnhandledException(raisedException);
              }
            }
            return; // no need to complain, will fix up constructor/lambda exceptions
          }
          break; // not handled anywhere, thus jump to error handling
        }
      } else if (traversedContext instanceof LoopingFlowContext) {
        if (abruptlyExitedLoops == null) {
          abruptlyExitedLoops = new ArrayList(5);
        }
        abruptlyExitedLoops.add(traversedContext);
      }
      if (remainingCount == 0) return;

      traversedContext.recordReturnFrom(flowInfo.unconditionalInits());

      if (traversedContext instanceof InsideSubRoutineFlowContext) {
        ASTNode node = traversedContext.associatedNode;
        if (node instanceof TryStatement) {
          TryStatement tryStatement = (TryStatement) node;
          flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits
        }
      }
      traversedContext = traversedContext.getLocalParent();
    }
    // if reaches this point, then there are some remaining unhandled exception types.
    nextReport:
    for (int i = 0; i < raisedCount; i++) {
      TypeBinding exception;
      if ((exception = raisedExceptions[i]) != null) {
        // only one complaint if same exception declared to be thrown more than once
        for (int j = 0; j < i; j++) {
          if (TypeBinding.equalsEquals(raisedExceptions[j], exception))
            continue nextReport; // already reported
        }
        scope.problemReporter().unhandledException(exception, location);
      }
    }
  }
 public boolean checkUnsafeCast(
     Scope scope,
     TypeBinding castType,
     TypeBinding expressionType,
     TypeBinding match,
     boolean isNarrowing) {
   if (TypeBinding.equalsEquals(match, castType)) {
     if (!isNarrowing
         && TypeBinding.equalsEquals(
             match,
             this.resolvedType
                 .leafComponentType()) // do not tag as unnecessary when recursing through upper
                                       // bounds
         && !(expressionType.isParameterizedType()
             && expressionType.isProvablyDistinct(castType))) {
       tagAsUnnecessaryCast(scope, castType);
     }
     return true;
   }
   if (match != null) {
     if (isNarrowing
         ? match.isProvablyDistinct(expressionType)
         : castType.isProvablyDistinct(match)) {
       return false;
     }
   }
   switch (castType.kind()) {
     case Binding.PARAMETERIZED_TYPE:
       if (!castType.isReifiable()) {
         if (match == null) { // unrelated types
           this.bits |= ASTNode.UnsafeCast;
           return true;
         }
         switch (match.kind()) {
           case Binding.PARAMETERIZED_TYPE:
             if (isNarrowing) {
               // [JLS 5.5] T <: S
               if (expressionType.isRawType() || !expressionType.isEquivalentTo(match)) {
                 this.bits |= ASTNode.UnsafeCast;
                 return true;
               }
               // [JLS 5.5] S has no subtype X != T, such that |X| == |T|
               // if I2<T,U> extends I1<T>, then cast from I1<T> to I2<T,U> is unchecked
               ParameterizedTypeBinding paramCastType = (ParameterizedTypeBinding) castType;
               ParameterizedTypeBinding paramMatch = (ParameterizedTypeBinding) match;
               // easy case if less parameters on match
               TypeBinding[] castArguments = paramCastType.arguments;
               int length = castArguments == null ? 0 : castArguments.length;
               if (paramMatch.arguments == null || length > paramMatch.arguments.length) {
                 this.bits |= ASTNode.UnsafeCast;
               } else if ((paramCastType.tagBits
                       & (TagBits.HasDirectWildcard | TagBits.HasTypeVariable))
                   != 0) {
                 // verify alternate cast type, substituting different type arguments
                 nextAlternateArgument:
                 for (int i = 0; i < length; i++) {
                   switch (castArguments[i].kind()) {
                     case Binding.WILDCARD_TYPE:
                     case Binding.TYPE_PARAMETER:
                       break; // check substituting with other
                     default:
                       continue nextAlternateArgument; // no alternative possible
                   }
                   TypeBinding[] alternateArguments;
                   // need to clone for each iteration to avoid env paramtype cache interference
                   System.arraycopy(
                       paramCastType.arguments,
                       0,
                       alternateArguments = new TypeBinding[length],
                       0,
                       length);
                   alternateArguments[i] = scope.getJavaLangObject();
                   LookupEnvironment environment = scope.environment();
                   ParameterizedTypeBinding alternateCastType =
                       environment.createParameterizedType(
                           (ReferenceBinding) castType.erasure(),
                           alternateArguments,
                           castType.enclosingType());
                   if (TypeBinding.equalsEquals(
                       alternateCastType.findSuperTypeOriginatingFrom(expressionType), match)) {
                     this.bits |= ASTNode.UnsafeCast;
                     break;
                   }
                 }
               }
               return true;
             } else {
               // [JLS 5.5] T >: S
               if (!match.isEquivalentTo(castType)) {
                 this.bits |= ASTNode.UnsafeCast;
                 return true;
               }
             }
             break;
           case Binding.RAW_TYPE:
             this.bits |=
                 ASTNode.UnsafeCast; // upcast since castType is known to be bound paramType
             return true;
           default:
             if (isNarrowing) {
               // match is not parameterized or raw, then any other subtype of match will erase  to
               // |T|
               this.bits |= ASTNode.UnsafeCast;
               return true;
             }
             break;
         }
       }
       break;
     case Binding.ARRAY_TYPE:
       TypeBinding leafType = castType.leafComponentType();
       if (isNarrowing && (!leafType.isReifiable() || leafType.isTypeVariable())) {
         this.bits |= ASTNode.UnsafeCast;
         return true;
       }
       break;
     case Binding.TYPE_PARAMETER:
       this.bits |= ASTNode.UnsafeCast;
       return true;
       //		(disabled) https://bugs.eclipse.org/bugs/show_bug.cgi?id=240807
       //		case Binding.TYPE :
       //			if (isNarrowing && match == null && expressionType.isParameterizedType()) {
       //				this.bits |= ASTNode.UnsafeCast;
       //				return true;
       //			}
       //			break;
   }
   if (!isNarrowing
       && TypeBinding.equalsEquals(
           match,
           this.resolvedType
               .leafComponentType())) { // do not tag as unnecessary when recursing through upper
                                        // bounds
     tagAsUnnecessaryCast(scope, castType);
   }
   return true;
 }
  /**
   * Check all parameters in methodSpec against the resolved role method. Also record which
   * parameters (including result) need translation (lifting/lowering).
   *
   * <p>Pre: not called if parameter mappings are present.
   *
   * @param methodSpec
   */
  protected boolean internalCheckParametersCompatibility(
      MethodSpec methodSpec, TypeBinding[] roleParams, TypeBinding[] baseParams) {
    if (baseParams.length < roleParams.length) {
      this.scope
          .problemReporter()
          .tooFewArgumentsInMethodMapping(this.roleMethodSpec, methodSpec, false /*callout*/);
      this.binding.tagBits |= TagBits.HasMappingIncompatibility;
      return false;
    } else {
      // before modifying the parameters array copy it:
      System.arraycopy(
          this.roleMethodSpec.parameters,
          0,
          this.roleMethodSpec.parameters = new TypeBinding[roleParams.length],
          0,
          roleParams.length);
      for (int j = 0; j < roleParams.length; j++) {
        TypeBinding baseParam = baseParams[j];
        TypeBinding roleParam = roleParams[j];
        if (baseParam.dimensions() != roleParam.dimensions()) {
          this.scope
              .problemReporter()
              .incompatibleMappedArgument(
                  baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false);
          this.binding.tagBits |= TagBits.HasMappingIncompatibility;
          continue; // no real type checking needed.
        }
        TypeBinding baseLeaf = baseParam.leafComponentType();
        TypeBinding roleLeaf = roleParam.leafComponentType();
        ASTNode location =
            (methodSpec.hasSignature) ? (ASTNode) methodSpec.arguments[j] : methodSpec;
        boolean compatibilityViaBaseAnchor = false;
        boolean hasReportedError = false;
        boolean isTypeVariable = false;
        try { // capture continue exits

          // unbound type variable matches everything:
          if (roleParam.isTypeVariable()) {
            TypeVariableBinding typeVariableBinding = (TypeVariableBinding) roleParam;
            if (typeVariableBinding.firstBound == null) continue;
            // use bound for type checking below, yet need not check two-way compatibility:
            isTypeVariable = true;
            roleLeaf = typeVariableBinding.firstBound.leafComponentType();
          }

          int dimensions = roleParam.dimensions();
          if (baseLeaf.isCompatibleWith(roleLeaf)) {
            this.roleMethodSpec.parameters[j] = roleParam;
            continue;
          }
          if (RoleTypeCreator.isCompatibleViaBaseAnchor(
              this.scope, baseLeaf, roleLeaf, TokenNameBINDIN)) {
            this.roleMethodSpec.parameters[j] = roleParam;
            compatibilityViaBaseAnchor = true;
            continue;
          }

          TypeBinding roleToLiftTo = null;
          if (isReplaceCallin()) {
            TypeBinding roleSideType = roleLeaf;
            if (roleSideType.isRole()) {
              ReferenceBinding roleRef = (ReferenceBinding) roleSideType;
              roleRef =
                  (ReferenceBinding)
                      TeamModel.strengthenRoleType(this.scope.enclosingReceiverType(), roleRef);
              if (TypeBinding.equalsEquals(roleRef.baseclass(), baseLeaf)) {
                if (dimensions > 0) {
                  if (roleRef instanceof DependentTypeBinding)
                    roleToLiftTo = ((DependentTypeBinding) roleRef).getArrayType(dimensions);
                  else
                    roleToLiftTo =
                        this.scope.createArrayType(roleRef, dimensions); // FIXME(SH): is this OK?
                } else {
                  roleToLiftTo = roleRef;
                }
              }
            }
          } else {
            // this uses OTJLD 2.3.3(a) adaptation which is not reversible, ie., not usable for
            // replace:
            roleToLiftTo =
                TeamModel.getRoleToLiftTo(this.scope, baseParam, roleParam, false, location);
          }
          if (roleToLiftTo != null) {
            // success by translation
            methodSpec.argNeedsTranslation[j] = true;
            this.roleMethodSpec.argNeedsTranslation[j] = true;
            this.roleMethodSpec.parameters[j] = roleToLiftTo; // this applies to all bindings

            // still need to check for ambiguity/abstract role:
            ReferenceBinding enclosingTeam = this.scope.enclosingSourceType().enclosingType();
            int iProblem =
                enclosingTeam
                    .getTeamModel()
                    .canLiftingFail((ReferenceBinding) roleToLiftTo.leafComponentType());
            if (iProblem > 0)
              addRoleLiftingProblem((ReferenceBinding) roleToLiftTo.leafComponentType(), iProblem);

            continue;
          }
          // check auto(un)boxing:
          if (this.scope.isBoxingCompatibleWith(baseLeaf, roleLeaf)) continue;

          if (roleParam instanceof ReferenceBinding) {
            ReferenceBinding roleRef = (ReferenceBinding) roleParam;
            if (roleRef.isRole() && roleRef.baseclass() != null) {
              this.scope
                  .problemReporter()
                  .typeMismatchErrorPotentialLift(
                      location, baseParam, roleParam, roleRef.baseclass());
              hasReportedError = true;
              continue;
            }
          }
          // no compatibility detected:
          this.scope
              .problemReporter()
              .incompatibleMappedArgument(
                  baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false);
          hasReportedError = true;
        } finally {
          if (hasReportedError) this.binding.tagBits |= TagBits.HasMappingIncompatibility;
          // regardless of continue, check this last because it is the least precise message:
          if (!hasReportedError && baseLeaf.isCompatibleWith(roleLeaf)) {
            if (isReplaceCallin() && !isTypeVariable) {
              boolean twowayCompatible =
                  compatibilityViaBaseAnchor
                      ? RoleTypeCreator.isCompatibleViaBaseAnchor(
                          this.scope, baseLeaf, roleLeaf, TokenNameBINDOUT)
                      : roleLeaf.isCompatibleWith(baseLeaf);
              if (!twowayCompatible) {
                // requires two-way compatibility (see additional paragraph in 4.5(d))
                this.scope
                    .problemReporter()
                    .typesNotTwowayCompatibleInReplace(baseParam, roleParam, location, j);
              }
            }
          }
        }
      }
    }
    return true; // unused in the callin case
  }
  private void searchVisibleFields(
      ReferenceBinding receiverType,
      Scope scope,
      InvocationSite invocationSite,
      Scope invocationScope,
      boolean onlyStaticFields,
      boolean notInJavadoc,
      ObjectVector localsFound,
      ObjectVector fieldsFound) {

    ReferenceBinding currentType = receiverType;
    ReferenceBinding[] interfacesToVisit = null;
    int nextPosition = 0;
    do {
      ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
      if (notInJavadoc && 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;
          }
        }
      }

      FieldBinding[] fields = currentType.availableFields();
      if (fields != null && fields.length > 0) {

        searchVisibleFields(
            fields,
            receiverType,
            scope,
            invocationSite,
            invocationScope,
            onlyStaticFields,
            localsFound,
            fieldsFound);
      }
      currentType = currentType.superclass();
    } while (notInJavadoc && currentType != null);

    if (notInJavadoc && interfacesToVisit != null) {
      for (int i = 0; i < nextPosition; i++) {
        ReferenceBinding anInterface = interfacesToVisit[i];
        FieldBinding[] fields = anInterface.availableFields();
        if (fields != null) {
          searchVisibleFields(
              fields,
              receiverType,
              scope,
              invocationSite,
              invocationScope,
              onlyStaticFields,
              localsFound,
              fieldsFound);
        }

        ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
        if (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;
          }
        }
      }
    }
  }