private TypeBinding internalResolveLeafType(
      Scope scope, ReferenceBinding enclosingType, boolean checkBounds) {
    ReferenceBinding currentType;
    if (enclosingType == null) {
      this.resolvedType = scope.getType(this.token);
      if (this.resolvedType.isValidBinding()) {
        currentType = (ReferenceBinding) this.resolvedType;
      } else {
        reportInvalidType(scope);
        switch (this.resolvedType.problemId()) {
          case ProblemReasons.NotFound:
          case ProblemReasons.NotVisible:
          case ProblemReasons.InheritedNameHidesEnclosingName:
            TypeBinding type = this.resolvedType.closestMatch();
            if (type instanceof ReferenceBinding) {
              currentType = (ReferenceBinding) type;
              break;
            }
            // $FALL-THROUGH$ - unable to complete type binding, but still resolve type arguments
          default:
            boolean isClassScope = scope.kind == Scope.CLASS_SCOPE;
            int argLength = this.typeArguments.length;
            for (int i = 0; i < argLength; i++) {
              TypeReference typeArgument = this.typeArguments[i];
              if (isClassScope) {
                typeArgument.resolveType((ClassScope) scope);
              } else {
                typeArgument.resolveType((BlockScope) scope, checkBounds);
              }
            }
            return null;
        }
        // be resilient, still attempt resolving arguments
      }
      enclosingType = currentType.enclosingType(); // if member type
      if (enclosingType != null) {
        enclosingType =
            currentType.isStatic()
                ? (ReferenceBinding)
                    scope
                        .environment()
                        .convertToRawType(
                            enclosingType, false /*do not force conversion of enclosing types*/)
                : scope.environment().convertToParameterizedType(enclosingType);
        currentType =
            scope
                .environment()
                .createParameterizedType(
                    (ReferenceBinding) currentType.erasure(), null /* no arg */, enclosingType);
      }
    } else { // resolving member type (relatively to enclosingType)
      this.resolvedType = currentType = scope.getMemberType(this.token, enclosingType);
      if (!this.resolvedType.isValidBinding()) {
        scope.problemReporter().invalidEnclosingType(this, currentType, enclosingType);
        return null;
      }
      if (isTypeUseDeprecated(currentType, scope))
        scope.problemReporter().deprecatedType(currentType, this);
      ReferenceBinding currentEnclosing = currentType.enclosingType();
      if (currentEnclosing != null && currentEnclosing.erasure() != enclosingType.erasure()) {
        enclosingType =
            currentEnclosing; // inherited member type, leave it associated with its enclosing
                              // rather than subtype
      }
    }

    // check generic and arity
    boolean isClassScope = scope.kind == Scope.CLASS_SCOPE;
    TypeReference keep = null;
    if (isClassScope) {
      keep = ((ClassScope) scope).superTypeReference;
      ((ClassScope) scope).superTypeReference = null;
    }
    int argLength = this.typeArguments.length;
    TypeBinding[] argTypes = new TypeBinding[argLength];
    boolean argHasError = false;
    ReferenceBinding currentOriginal = (ReferenceBinding) currentType.original();
    for (int i = 0; i < argLength; i++) {
      TypeReference typeArgument = this.typeArguments[i];
      TypeBinding argType =
          isClassScope
              ? typeArgument.resolveTypeArgument((ClassScope) scope, currentOriginal, i)
              : typeArgument.resolveTypeArgument((BlockScope) scope, currentOriginal, i);
      this.bits |= (typeArgument.bits & ASTNode.HasTypeAnnotations);
      if (argType == null) {
        argHasError = true;
      } else {
        if (typeArgument.annotations != null)
          argTypes[i] =
              captureTypeAnnotations(scope, enclosingType, argType, typeArgument.annotations[0]);
        else argTypes[i] = argType;
      }
    }
    if (argHasError) {
      return null;
    }
    if (isClassScope) {
      ((ClassScope) scope).superTypeReference = keep;
      if (((ClassScope) scope).detectHierarchyCycle(currentOriginal, this)) return null;
    }

    final boolean isDiamond = (this.bits & ASTNode.IsDiamond) != 0;
    TypeVariableBinding[] typeVariables = currentOriginal.typeVariables();
    if (typeVariables == Binding.NO_TYPE_VARIABLES) { // non generic invoked with arguments
      boolean isCompliant15 =
          scope.compilerOptions().originalSourceLevel >= ClassFileConstants.JDK1_5;
      if ((currentOriginal.tagBits & TagBits.HasMissingType) == 0) {
        if (isCompliant15) { // below 1.5, already reported as syntax error
          this.resolvedType = currentType;
          scope
              .problemReporter()
              .nonGenericTypeCannotBeParameterized(0, this, currentType, argTypes);
          return null;
        }
      }
      // resilience do not rebuild a parameterized type unless compliance is allowing it
      if (!isCompliant15) {
        if (!this.resolvedType.isValidBinding()) return currentType;
        return this.resolvedType = currentType;
      }
      // if missing generic type, and compliance >= 1.5, then will rebuild a parameterized binding
    } else if (argLength != typeVariables.length) {
      if (!isDiamond) { // check arity, IsDiamond never set for 1.6-
        scope.problemReporter().incorrectArityForParameterizedType(this, currentType, argTypes);
        return null;
      }
    } else if (!currentType.isStatic()) {
      ReferenceBinding actualEnclosing = currentType.enclosingType();
      if (actualEnclosing != null && actualEnclosing.isRawType()) {
        scope
            .problemReporter()
            .rawMemberTypeCannotBeParameterized(
                this,
                scope.environment().createRawType(currentOriginal, actualEnclosing),
                argTypes);
        return null;
      }
    }

    ParameterizedTypeBinding parameterizedType =
        scope.environment().createParameterizedType(currentOriginal, argTypes, enclosingType);
    // check argument type compatibility for non <> cases - <> case needs no bounds check, we will
    // scream foul if needed during inference.
    if (!isDiamond) {
      if (checkBounds) // otherwise will do it in Scope.connectTypeVariables() or generic method
                       // resolution
      parameterizedType.boundCheck(scope, this.typeArguments);
      else scope.deferBoundCheck(this);
    }
    if (isTypeUseDeprecated(parameterizedType, scope))
      reportDeprecatedType(parameterizedType, scope);

    if (!this.resolvedType.isValidBinding()) {
      return parameterizedType;
    }
    return this.resolvedType = parameterizedType;
  }