private void addUsage(final ReductionSystem system, final PsiElement element) {

    if (element instanceof PsiVariable) {
      final PsiExpression initializer = ((PsiVariable)element).getInitializer();

      if (initializer != null) {
        final PsiExpression core = PsiUtil.deparenthesizeExpression(initializer);

        if (core instanceof PsiArrayInitializerExpression) {
          final PsiExpression[] inits = ((PsiArrayInitializerExpression)core).getInitializers();
          final PsiType type = getType(element);

          for (PsiExpression init : inits) {
            system.addSubtypeConstraint(evaluateType(init, system).createArrayType(), type);
          }
        }
        else if (core instanceof PsiNewExpression) {
          final PsiArrayInitializerExpression init = ((PsiNewExpression)core).getArrayInitializer();

          if (init != null) {
            final PsiExpression[] inits = init.getInitializers();
            final PsiType type = getType(element);

            for (PsiExpression init1 : inits) {
              system.addSubtypeConstraint(evaluateType(init1, system).createArrayType(), type);
            }
          }

          system.addSubtypeConstraint(evaluateType(core, system), getType(element));
        }
        else {
          system.addSubtypeConstraint(evaluateType(core, system), getType(element));
        }
      }

      if (element instanceof PsiParameter parameter) {
        final PsiElement declarationScope = parameter.getDeclarationScope();
        if (declarationScope instanceof PsiMethod method) {
          final PsiSearchHelper helper = PsiSearchHelper.getInstance(myManager.getProject());
          SearchScope scope = getScope(helper, method);

          for (PsiReference ref : ReferencesSearch.search(method, scope, true)) {
            final PsiElement elt = ref.getElement();

            final PsiCallExpression call = PsiTreeUtil.getParentOfType(elt, PsiCallExpression.class);

            if (call != null) {
              PsiExpressionList argList = call.getArgumentList();
              if (argList != null) {
                PsiExpression[] args = argList.getExpressions();
                int index = method.getParameterList().getParameterIndex(parameter);
                if (index < args.length) {
                  system.addSubtypeConstraint(evaluateType(args[index], system), myTypes.get(element));
                }
              }
            }
          }
        } else if (declarationScope instanceof PsiForeachStatement) {
          addForEachConstraint(system, (PsiForeachStatement)declarationScope);
        }
      }
      return;
    }
    else if (element instanceof PsiMethod) {
      final PsiType reType = getType(element);

      element.accept(new JavaRecursiveElementWalkingVisitor() {
        @Override public void visitReturnStatement(final @NotNull PsiReturnStatement statement) {
          super.visitReturnStatement(statement);

          final PsiExpression retExpr = statement.getReturnValue();

          if (retExpr != null) {
            system.addSubtypeConstraint(evaluateType(retExpr, system), reType);
          }
        }

        @Override
        public void visitClass(@NotNull PsiClass aClass) {}

        @Override
        public void visitLambdaExpression(@NotNull PsiLambdaExpression expression) {}
      });

      return;
    }

    final PsiElement root = PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiField.class);

    if (root != null) {
      final PsiAnchor anchor = PsiAnchor.create(root);

      if (!myVisitedConstructions.contains(anchor)) {
        //return from lambda is processed inside visitReturnStatement
        //noinspection UnsafeReturnStatementVisitor
        root.accept(new JavaRecursiveElementWalkingVisitor() {
          @Override public void visitAssignmentExpression(final @NotNull PsiAssignmentExpression expression) {
            super.visitAssignmentExpression(expression);

            system
              .addSubtypeConstraint(evaluateType(expression.getRExpression(), system), evaluateType(expression.getLExpression(), system));
          }

          @Override public void visitConditionalExpression(final @NotNull PsiConditionalExpression expression) {
            super.visitConditionalExpression(expression);

            system.addSubtypeConstraint(evaluateType(expression.getThenExpression(), system),
                                        evaluateType(expression.getElseExpression(), system));
            system.addSubtypeConstraint(evaluateType(expression.getElseExpression(), system),
                                        evaluateType(expression.getThenExpression(), system));
          }

          @Override public void visitCallExpression(final @NotNull PsiCallExpression expression) {
            super.visitCallExpression(expression);
            evaluateType(expression, system);
          }

          @Override public void visitReturnStatement(final @NotNull PsiReturnStatement statement) {
            super.visitReturnStatement(statement);

            final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, true, PsiLambdaExpression.class);
            if (method != null) {
              system.addSubtypeConstraint(evaluateType(statement.getReturnValue(), system), getType(method));
            }
          }

          @Override public void visitTypeCastExpression(final @NotNull PsiTypeCastExpression expression) {
            super.visitTypeCastExpression(expression);

            final PsiType operandType = evaluateType(expression.getOperand(), system);
            final PsiType castType = evaluateType(expression, system);
            if (operandType == null || castType == null) return;

            if (Util.bindsTypeVariables(operandType)) {
              system.addCast(expression, operandType);
            }

            if (operandType.getDeepComponentType() instanceof PsiTypeVariable ||
                castType.getDeepComponentType() instanceof PsiTypeVariable) {
              system.addSubtypeConstraint(operandType, castType);
            }
            else {
              final PsiClassType.ClassResolveResult operandResult = Util.resolveType(operandType);
              final PsiClassType.ClassResolveResult castResult = Util.resolveType(castType);

              final PsiClass operandClass = operandResult.getElement();
              final PsiClass castClass = castResult.getElement();

              if (operandClass != null && castClass != null) {
                if (InheritanceUtil.isInheritorOrSelf(operandClass, castClass, true)) {
                  system.addSubtypeConstraint(operandType, castType);
                }
              }
            }
          }

          @Override public void visitVariable(final @NotNull PsiVariable variable) {
            super.visitVariable(variable);

            final PsiExpression init = variable.getInitializer();

            if (init != null) {
              system.addSubtypeConstraint(evaluateType(init, system), getType(variable));
            }
          }

          @Override public void visitNewExpression(final @NotNull PsiNewExpression expression) {
            super.visitNewExpression(expression);

            final PsiArrayInitializerExpression init = expression.getArrayInitializer();

            if (init != null) {
              final PsiExpression[] inits = init.getInitializers();
              final PsiType type = getType(expression);

              for (PsiExpression init1 : inits) {
                system.addSubtypeConstraint(evaluateType(init1, system).createArrayType(), type);
              }
            }
          }

          @Override public void visitReferenceExpression(final @NotNull PsiReferenceExpression expression) {
            final PsiExpression qualifierExpression = expression.getQualifierExpression();

            if (qualifierExpression != null) {
              qualifierExpression.accept(this);
            }
          }
        });

        myVisitedConstructions.add(anchor);
      }
    }
  }
private void addUsage(final ReductionSystem system, final PsiElement element)