public static boolean proveArrayTypeDistinct(Project project, PsiArrayType type, PsiType bound) {
    final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
    final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);
    final Set<PsiClass> possibleClasses = new HashSet<PsiClass>();
    possibleClasses.add(facade.findClass(CommonClassNames.JAVA_IO_SERIALIZABLE, searchScope));
    possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_CLONEABLE, searchScope));
    possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, searchScope));

    if (type.getArrayDimensions() == bound.getArrayDimensions()) {
      final PsiType componentType = type.getComponentType();
      final PsiType boundComponentType = ((PsiArrayType) bound).getComponentType();
      if (boundComponentType instanceof PsiClassType && componentType instanceof PsiClassType) {
        return proveExtendsBoundsDistinct(
            boundComponentType,
            componentType,
            ((PsiClassType) boundComponentType).resolve(),
            ((PsiClassType) componentType).resolve());
      } else {
        return !bound.equals(type);
      }
    } else if (bound.getArrayDimensions() + 1 == type.getArrayDimensions()
        && bound.getDeepComponentType() instanceof PsiClassType) {
      return !possibleClasses.contains(((PsiClassType) bound.getDeepComponentType()).resolve());
    } else if (bound.getArrayDimensions() == type.getArrayDimensions() + 1
        && type.getDeepComponentType() instanceof PsiClassType) {
      return !possibleClasses.contains(((PsiClassType) type.getDeepComponentType()).resolve());
    } else if (bound instanceof PsiClassType) {
      return !possibleClasses.contains(((PsiClassType) bound).resolve());
    } else if (bound instanceof PsiWildcardType) {
      final PsiType boundBound = ((PsiWildcardType) bound).getBound();
      return boundBound != null && !boundBound.equals(type);
    }
    return true;
  }
 private void checkExpression(PsiExpression expression) {
   if (expression.getParent() instanceof PsiParenthesizedExpression) {
     return;
   }
   final PsiType expressionType = expression.getType();
   if (expressionType == null) {
     return;
   }
   if (expressionType.getArrayDimensions() > 0) {
     // a horrible hack to get around what happens when you pass
     // an array to a vararg expression
     return;
   }
   if (TypeConversionUtil.isPrimitiveAndNotNull(expressionType)) {
     return;
   }
   final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(expressionType);
   if (unboxedType == null) {
     return;
   }
   final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
   if (expectedType == null) {
     return;
   }
   if (!TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) {
     return;
   }
   if (!expectedType.isAssignableFrom(unboxedType)) {
     return;
   }
   registerError(expression, expression);
 }
  private static void generateNamesForArrayType(
      PsiType type, Set<String> possibleNames, NameValidator validator) {
    int arrayDim = type.getArrayDimensions();
    if (arrayDim == 0) return;
    PsiType deepType = type.getDeepComponentType();
    String candidateName = cleanTypeName(deepType.getPresentableText());
    if (deepType instanceof PsiClassType) {
      PsiClass clazz = ((PsiClassType) deepType).resolve();
      if (clazz == null) return;
      candidateName = GroovyNamesUtil.fromLowerLetter(clazz.getName());
    }
    candidateName = StringUtil.pluralize(GroovyNamesUtil.fromLowerLetter(candidateName));
    generateCamelNames(possibleNames, validator, candidateName);

    ArrayList<String> camelizedName = GroovyNamesUtil.camelizeString(candidateName);
    candidateName = camelizedName.get(camelizedName.size() - 1);
    candidateName = "arrayOf" + fromUpperLetter(candidateName);
    possibleNames.add(validator.validateName(candidateName, true));
  }
  @NotNull
  private DfaValue getMethodResultValue(
      MethodCallInstruction instruction,
      @Nullable DfaValue qualifierValue,
      DfaValueFactory factory) {
    DfaValue precalculated = instruction.getPrecalculatedReturnValue();
    if (precalculated != null) {
      return precalculated;
    }

    final PsiType type = instruction.getResultType();
    final MethodCallInstruction.MethodType methodType = instruction.getMethodType();

    if (methodType == MethodCallInstruction.MethodType.UNBOXING) {
      return factory.getBoxedFactory().createUnboxed(qualifierValue);
    }

    if (methodType == MethodCallInstruction.MethodType.BOXING) {
      DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue);
      return boxed == null ? factory.createTypeValue(type, Nullness.NOT_NULL) : boxed;
    }

    if (methodType == MethodCallInstruction.MethodType.CAST) {
      assert qualifierValue != null;
      if (qualifierValue instanceof DfaConstValue) {
        Object casted =
            TypeConversionUtil.computeCastTo(((DfaConstValue) qualifierValue).getValue(), type);
        return factory
            .getConstFactory()
            .createFromValue(casted, type, ((DfaConstValue) qualifierValue).getConstant());
      }
      return qualifierValue;
    }

    if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) {
      Nullness nullability = myReturnTypeNullability.get(instruction);
      if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) {
        nullability = Nullness.NULLABLE;
      }
      return factory.createTypeValue(type, nullability);
    }
    return DfaUnknownValue.getInstance();
  }
 @Override
 public void visitLiteralExpression(@NotNull PsiLiteralExpression value) {
   super.visitLiteralExpression(value);
   final String text = value.getText();
   if (!PsiKeyword.NULL.equals(text)) {
     return;
   }
   PsiElement parent = value.getParent();
   while (parent instanceof PsiParenthesizedExpression
       || parent instanceof PsiConditionalExpression
       || parent instanceof PsiTypeCastExpression) {
     parent = parent.getParent();
   }
   if (parent == null || !(parent instanceof PsiReturnStatement)) {
     return;
   }
   final PsiMethod method = PsiTreeUtil.getParentOfType(value, PsiMethod.class);
   if (method == null) {
     return;
   }
   final PsiType returnType = method.getReturnType();
   if (returnType == null) {
     return;
   }
   final boolean isArray = returnType.getArrayDimensions() > 0;
   if (AnnotationUtil.isAnnotated(method, AnnotationUtil.NULLABLE, false)) {
     return;
   }
   if (m_reportCollectionMethods && CollectionUtils.isCollectionClassOrInterface(returnType)) {
     registerError(value, value);
   } else if (m_reportArrayMethods && isArray) {
     registerError(value, value);
   } else if (m_reportObjectMethods && !isArray) {
     registerError(value, value);
   }
 }
 private boolean checkCondition(
     @Nullable PsiExpression condition,
     @NotNull PsiStatement context,
     List<PsiExpression> notUpdated) {
   if (condition == null) {
     return false;
   }
   if (PsiUtil.isConstantExpression(condition) || PsiKeyword.NULL.equals(condition.getText())) {
     return true;
   }
   if (condition instanceof PsiInstanceOfExpression) {
     final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression) condition;
     final PsiExpression operand = instanceOfExpression.getOperand();
     return checkCondition(operand, context, notUpdated);
   } else if (condition instanceof PsiParenthesizedExpression) {
     // catch stuff like "while ((x)) { ... }"
     final PsiExpression expression = ((PsiParenthesizedExpression) condition).getExpression();
     return checkCondition(expression, context, notUpdated);
   } else if (condition instanceof PsiPolyadicExpression) {
     // while (value != x) { ... }
     // while (value != (x + y)) { ... }
     // while (b1 && b2) { ... }
     final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression) condition;
     for (PsiExpression operand : polyadicExpression.getOperands()) {
       if (!checkCondition(operand, context, notUpdated)) {
         return false;
       }
     }
     return true;
   } else if (condition instanceof PsiReferenceExpression) {
     final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) condition;
     final PsiElement element = referenceExpression.resolve();
     if (element instanceof PsiField) {
       final PsiField field = (PsiField) element;
       final PsiType type = field.getType();
       if (field.hasModifierProperty(PsiModifier.FINAL) && type.getArrayDimensions() == 0) {
         if (field.hasModifierProperty(PsiModifier.STATIC)) {
           return true;
         }
         final PsiExpression qualifier = referenceExpression.getQualifierExpression();
         if (qualifier == null) {
           return true;
         } else if (checkCondition(qualifier, context, notUpdated)) {
           return true;
         }
       }
     } else if (element instanceof PsiVariable) {
       final PsiVariable variable = (PsiVariable) element;
       if (variable.hasModifierProperty(PsiModifier.FINAL)) {
         // final variables cannot be updated, don't bother to
         // flag them
         return true;
       } else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
         if (!VariableAccessUtils.variableIsAssigned(variable, context)) {
           notUpdated.add(referenceExpression);
           return true;
         }
       }
     }
   } else if (condition instanceof PsiPrefixExpression) {
     final PsiPrefixExpression prefixExpression = (PsiPrefixExpression) condition;
     final IElementType tokenType = prefixExpression.getOperationTokenType();
     if (JavaTokenType.EXCL.equals(tokenType)
         || JavaTokenType.PLUS.equals(tokenType)
         || JavaTokenType.MINUS.equals(tokenType)) {
       final PsiExpression operand = prefixExpression.getOperand();
       return checkCondition(operand, context, notUpdated);
     }
   } else if (condition instanceof PsiArrayAccessExpression) {
     // Actually the contents of the array could change nevertheless
     // if it is accessed through a different reference like this:
     //   int[] local_ints = new int[]{1, 2};
     //   int[] other_ints = local_ints;
     //   while (local_ints[0] > 0) { other_ints[0]--; }
     //
     // Keep this check?
     final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression) condition;
     final PsiExpression indexExpression = accessExpression.getIndexExpression();
     return checkCondition(indexExpression, context, notUpdated)
         && checkCondition(accessExpression.getArrayExpression(), context, notUpdated);
   } else if (condition instanceof PsiConditionalExpression) {
     final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) condition;
     final PsiExpression thenExpression = conditionalExpression.getThenExpression();
     final PsiExpression elseExpression = conditionalExpression.getElseExpression();
     if (thenExpression == null || elseExpression == null) {
       return false;
     }
     return checkCondition(conditionalExpression.getCondition(), context, notUpdated)
         && checkCondition(thenExpression, context, notUpdated)
         && checkCondition(elseExpression, context, notUpdated);
   } else if (condition instanceof PsiThisExpression) {
     return true;
   } else if (condition instanceof PsiMethodCallExpression && !ignoreIterators) {
     final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) condition;
     if (!IteratorUtils.isCallToHasNext(methodCallExpression)) {
       return false;
     }
     final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
     final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
     if (qualifierExpression instanceof PsiReferenceExpression) {
       final PsiReferenceExpression referenceExpression =
           (PsiReferenceExpression) qualifierExpression;
       final PsiElement element = referenceExpression.resolve();
       if (!(element instanceof PsiVariable)) {
         return false;
       }
       final PsiVariable variable = (PsiVariable) element;
       if (!IteratorUtils.containsCallToScannerNext(context, variable, true)) {
         notUpdated.add(qualifierExpression);
         return true;
       }
     } else {
       if (!IteratorUtils.containsCallToScannerNext(context, null, true)) {
         notUpdated.add(methodCallExpression);
         return true;
       }
     }
   }
   return false;
 }
 @Override
 public void visitReferenceExpression(PsiReferenceExpression expression) {
   if (myWritten && myRead) {
     return;
   }
   super.visitReferenceExpression(expression);
   final PsiElement target = expression.resolve();
   if (target != myVariable) {
     return;
   }
   if (PsiUtil.isAccessedForWriting(expression)) {
     final PsiElement parent =
         PsiTreeUtil.skipParentsOfType(expression, PsiParenthesizedExpression.class);
     if (parent instanceof PsiAssignmentExpression) {
       final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) parent;
       final PsiExpression rhs = assignmentExpression.getRExpression();
       if (isComplexArrayExpression(rhs)) {
         myWritten = true;
         myRead = true;
       } else if (!isSimpleArrayExpression(rhs)) {
         myWritten = true;
       }
     }
     return;
   }
   myIsReferenced = true;
   PsiElement parent = getParent(expression);
   if (parent instanceof PsiArrayAccessExpression) {
     PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression) parent;
     parent = getParent(parent);
     while (parent instanceof PsiArrayAccessExpression) {
       arrayAccessExpression = (PsiArrayAccessExpression) parent;
       parent = getParent(parent);
     }
     final PsiType type = arrayAccessExpression.getType();
     if (type != null) {
       final int dimensions = type.getArrayDimensions();
       if (dimensions > 0 && dimensions != myVariable.getType().getArrayDimensions()) {
         myWritten = true;
       }
     }
     if (PsiUtil.isAccessedForWriting(arrayAccessExpression)) {
       myWritten = true;
     }
     if (PsiUtil.isAccessedForReading(arrayAccessExpression)) {
       myRead = true;
     }
   } else if (parent instanceof PsiReferenceExpression) {
     final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) parent;
     final String name = referenceExpression.getReferenceName();
     if ("length".equals(name)
         || ("clone".equals(name)
             && referenceExpression.getParent() instanceof PsiMethodCallExpression)) {
       myRead = true;
     }
   } else if (parent instanceof PsiForeachStatement) {
     final PsiForeachStatement foreachStatement = (PsiForeachStatement) parent;
     final PsiExpression iteratedValue = foreachStatement.getIteratedValue();
     if (PsiTreeUtil.isAncestor(iteratedValue, expression, false)) {
       myRead = true;
     }
   } else if (parent instanceof PsiExpressionList) {
     final PsiExpressionList expressionList = (PsiExpressionList) parent;
     parent = parent.getParent();
     if (parent instanceof PsiMethodCallExpression) {
       final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) parent;
       final PsiMethod method = methodCallExpression.resolveMethod();
       if (method != null) {
         final PsiClass aClass = method.getContainingClass();
         if (aClass != null) {
           final String methodName = method.getName();
           final String qualifiedName = aClass.getQualifiedName();
           if ("java.lang.System".equals(qualifiedName)) {
             if ("arraycopy".equals(methodName)) {
               final PsiExpression[] expressions = expressionList.getExpressions();
               if (expressions.length == 5) {
                 if (PsiTreeUtil.isAncestor(expressions[0], expression, false)) {
                   myRead = true;
                   return;
                 } else if (PsiTreeUtil.isAncestor(expressions[2], expression, false)) {
                   myWritten = true;
                   return;
                 }
               }
             }
           } else if (CommonClassNames.JAVA_UTIL_ARRAYS.equals(qualifiedName)) {
             if ("fill".equals(methodName)
                 || "parallelPrefix".equals(methodName)
                 || "parallelSetAll".equals(methodName)
                 || "parallelSort".equals(methodName)
                 || "setAll".equals(methodName)
                 || "sort".equals(methodName)) {
               myWritten = true;
             } else {
               myRead = true;
             }
             return;
           }
         }
       }
     }
     myRead = true;
     myWritten = true;
   } else {
     myWritten = true;
     myRead = true;
   }
 }
  private static void changeNewOperatorType(
      PsiNewExpression originalExpression, PsiType toType, final Editor editor)
      throws IncorrectOperationException {
    PsiNewExpression newExpression;
    PsiElementFactory factory =
        JavaPsiFacade.getInstance(originalExpression.getProject()).getElementFactory();
    int caretOffset;
    TextRange selection;
    if (toType instanceof PsiArrayType) {
      final PsiExpression[] originalExpressionArrayDimensions =
          originalExpression.getArrayDimensions();
      caretOffset = 0;
      @NonNls String text = "new " + toType.getDeepComponentType().getCanonicalText() + "[";
      if (originalExpressionArrayDimensions.length > 0) {
        text += originalExpressionArrayDimensions[0].getText();
      } else {
        text += "0";
        caretOffset = -2;
      }
      text += "]";
      for (int i = 1; i < toType.getArrayDimensions(); i++) {
        text += "[";
        String arrayDimension = "";
        if (originalExpressionArrayDimensions.length > i) {
          arrayDimension = originalExpressionArrayDimensions[i].getText();
          text += arrayDimension;
        }
        text += "]";
        if (caretOffset < 0) {
          caretOffset -= arrayDimension.length() + 2;
        }
      }

      newExpression = (PsiNewExpression) factory.createExpressionFromText(text, originalExpression);
      if (caretOffset < 0) {
        selection = new TextRange(caretOffset, caretOffset + 1);
      } else {
        selection = null;
      }
    } else {
      final PsiAnonymousClass anonymousClass = originalExpression.getAnonymousClass();
      newExpression =
          (PsiNewExpression)
              factory.createExpressionFromText(
                  "new " + toType.getCanonicalText() + "()" + (anonymousClass != null ? "{}" : ""),
                  originalExpression);
      PsiExpressionList argumentList = originalExpression.getArgumentList();
      if (argumentList == null) return;
      newExpression.getArgumentList().replace(argumentList);
      if (anonymousClass == null) { // just to prevent useless inference
        if (PsiDiamondTypeUtil.canCollapseToDiamond(newExpression, originalExpression, toType)) {
          final PsiElement paramList =
              PsiDiamondTypeUtil.replaceExplicitWithDiamond(
                  newExpression.getClassOrAnonymousClassReference().getParameterList());
          newExpression = PsiTreeUtil.getParentOfType(paramList, PsiNewExpression.class);
        }
      }

      if (anonymousClass != null) {
        final PsiAnonymousClass newAnonymousClass =
            (PsiAnonymousClass) newExpression.getAnonymousClass().replace(anonymousClass);
        final PsiClass aClass = PsiUtil.resolveClassInType(toType);
        assert aClass != null;
        newAnonymousClass
            .getBaseClassReference()
            .replace(factory.createClassReferenceElement(aClass));
      }
      selection = null;
      caretOffset = -1;
    }
    PsiElement element = originalExpression.replace(newExpression);
    editor.getCaretModel().moveToOffset(element.getTextRange().getEndOffset() + caretOffset);
    editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
    if (selection != null) {
      selection = selection.shiftRight(element.getTextRange().getEndOffset());
      editor.getSelectionModel().setSelection(selection.getStartOffset(), selection.getEndOffset());
    }
  }
Example #9
0
  public static void processSubTypes(
      PsiType psiType,
      final PsiElement context,
      boolean getRawSubtypes,
      @NotNull final PrefixMatcher matcher,
      Consumer<PsiType> consumer) {
    int arrayDim = psiType.getArrayDimensions();

    psiType = psiType.getDeepComponentType();
    if (!(psiType instanceof PsiClassType)) return;

    final Condition<String> shortNameCondition =
        new Condition<String>() {
          @Override
          public boolean value(String s) {
            return matcher.prefixMatches(s);
          }
        };

    final PsiClassType baseType = (PsiClassType) psiType;
    final PsiClassType.ClassResolveResult baseResult =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<PsiClassType.ClassResolveResult>() {
                  @Override
                  public PsiClassType.ClassResolveResult compute() {
                    return JavaCompletionUtil.originalize(baseType).resolveGenerics();
                  }
                });
    final PsiClass baseClass = baseResult.getElement();
    final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor();
    if (baseClass == null) return;

    final GlobalSearchScope scope =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<GlobalSearchScope>() {
                  @Override
                  public GlobalSearchScope compute() {
                    return context.getResolveScope();
                  }
                });

    final Processor<PsiClass> inheritorsProcessor =
        createInheritorsProcessor(
            context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor);
    if (matcher.getPrefix().length() > 2) {
      AllClassesGetter.processJavaClasses(
          matcher,
          context.getProject(),
          scope,
          new Processor<PsiClass>() {
            @Override
            public boolean process(PsiClass psiClass) {
              if (psiClass.isInheritor(baseClass, true)) {
                return inheritorsProcessor.process(psiClass);
              }
              return true;
            }
          });
    } else {
      final Query<PsiClass> baseQuery =
          ClassInheritorsSearch.search(
              new ClassInheritorsSearch.SearchParameters(
                  baseClass, scope, true, false, false, shortNameCondition));
      final Query<PsiClass> query =
          new FilteredQuery<PsiClass>(
              baseQuery,
              new Condition<PsiClass>() {
                @Override
                public boolean value(final PsiClass psiClass) {
                  return !(psiClass instanceof PsiTypeParameter);
                }
              });
      query.forEach(inheritorsProcessor);
    }
  }
  @Nullable
  public static PsiType getVariableTypeByExpressionType(
      @Nullable PsiType type, final boolean openCaptured) {
    if (type == null) return null;
    if (type instanceof PsiCapturedWildcardType) {
      type = ((PsiCapturedWildcardType) type).getWildcard();
    }
    PsiType transformed =
        type.accept(
            new PsiTypeVisitor<PsiType>() {
              @Override
              public PsiType visitArrayType(PsiArrayType arrayType) {
                PsiType componentType = arrayType.getComponentType();
                PsiType type = componentType.accept(this);
                if (type == componentType) return arrayType;
                return type.createArrayType();
              }

              @Override
              public PsiType visitType(PsiType type) {
                return type;
              }

              @Override
              public PsiType visitWildcardType(final PsiWildcardType wildcardType) {
                final PsiType bound = wildcardType.getBound();
                PsiManager manager = wildcardType.getManager();
                if (bound != null) {
                  final PsiType acceptedBound = bound.accept(this);
                  if (acceptedBound instanceof PsiWildcardType) {
                    if (((PsiWildcardType) acceptedBound).isExtends() != wildcardType.isExtends())
                      return PsiWildcardType.createUnbounded(manager);
                    return acceptedBound;
                  }
                  if (wildcardType.isExtends()
                      && acceptedBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT))
                    return PsiWildcardType.createUnbounded(manager);
                  if (acceptedBound.equals(bound)) return wildcardType;
                  return wildcardType.isExtends()
                      ? PsiWildcardType.createExtends(manager, acceptedBound)
                      : PsiWildcardType.createSuper(manager, acceptedBound);
                }
                return wildcardType;
              }

              @Override
              public PsiType visitCapturedWildcardType(
                  PsiCapturedWildcardType capturedWildcardType) {
                return openCaptured
                    ? capturedWildcardType.getWildcard().accept(this)
                    : capturedWildcardType;
              }

              @Override
              public PsiType visitClassType(PsiClassType classType) {
                PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
                PsiClass aClass = resolveResult.getElement();
                if (aClass == null) return classType;
                boolean toExtend = false;
                PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
                for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) {
                  PsiType typeArgument = resolveResult.getSubstitutor().substitute(typeParameter);
                  if (typeArgument instanceof PsiCapturedWildcardType) toExtend = true;
                  if (typeArgument instanceof PsiWildcardType
                      && ((PsiWildcardType) typeArgument).getBound()
                          instanceof PsiIntersectionType) {
                    toExtend = true;
                  }
                  PsiType toPut;
                  if (typeArgument == null) {
                    toPut = null;
                  } else {
                    final PsiType accepted = typeArgument.accept(this);
                    if (typeArgument instanceof PsiIntersectionType) {
                      toPut = PsiWildcardType.createExtends(typeParameter.getManager(), accepted);
                    } else {
                      toPut = accepted;
                    }
                  }
                  LOG.assertTrue(toPut == null || toPut.isValid(), toPut);
                  substitutor = substitutor.put(typeParameter, toPut);
                }
                final PsiAnnotation[] applicableAnnotations = classType.getApplicableAnnotations();
                if (substitutor == PsiSubstitutor.EMPTY
                    && !toExtend
                    && applicableAnnotations.length == 0
                    && !(aClass instanceof PsiTypeParameter)) return classType;
                PsiManager manager = aClass.getManager();
                PsiType result =
                    JavaPsiFacade.getInstance(manager.getProject())
                        .getElementFactory()
                        .createType(
                            aClass,
                            substitutor,
                            PsiUtil.getLanguageLevel(aClass),
                            applicableAnnotations);
                if (toExtend) result = PsiWildcardType.createExtends(manager, result);
                return result;
              }
            });

    PsiType componentType = transformed != null ? transformed.getDeepComponentType() : null;
    if (componentType instanceof PsiWildcardType) {
      componentType = ((PsiWildcardType) componentType).getExtendsBound();
      int dims = transformed.getArrayDimensions();
      for (int i = 0; i < dims; i++) componentType = componentType.createArrayType();
      return componentType;
    }

    return transformed;
  }