static boolean processUsagesFlownDownTo(@NotNull PsiElement expression,
                                          @NotNull Processor<? super SliceUsage> processor,
                                          @NotNull JavaSliceBuilder builder) {
    expression = JavaSliceUsage.simplify(expression);
    PsiElement original = expression;
    if (expression instanceof PsiArrayAccessExpression) {
      // now start tracking the array instead of element
      expression = ((PsiArrayAccessExpression)expression).getArrayExpression();
      builder = builder.incrementNesting();
    }
    PsiElement par = expression.getParent();
    if (expression instanceof PsiExpressionList && par instanceof PsiMethodCallExpression) {
      // expression list ends up here if we track varargs
      PsiMethod method = ((PsiMethodCallExpression)par).resolveMethod();
      if (method != null) {
        int parametersCount = method.getParameterList().getParametersCount();
        if (parametersCount != 0) {
          // unfold varargs list into individual expressions
          PsiExpression[] expressions = ((PsiExpressionList)expression).getExpressions();
          if (builder.hasNesting()) {
            // should skip not-vararg arguments
            for (int i = parametersCount-1; i < expressions.length; i++) {
              PsiExpression arg = expressions[i];
              if (!builder.decrementNesting().process(arg, processor)) return false;
            }
          }
          return true;
        }
      }
    }

    boolean needToReportDeclaration = false;
    if (expression instanceof PsiReferenceExpression ref) {
      PsiElement element = SliceForwardUtil.complexify(expression);
      if (element instanceof PsiExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression)element)) {
        PsiExpression rightSide = ((PsiAssignmentExpression)element.getParent()).getRExpression();
        return rightSide == null || builder.process(rightSide, processor);
      }
      JavaResolveResult result = ref.advancedResolve(false);
      builder = builder.withSubstitutor(result.getSubstitutor().putAll(builder.getSubstitutor()));
      PsiElement resolved = result.getElement();
      if (resolved instanceof PsiCompiledElement) {
        resolved = resolved.getNavigationElement();
      }
      if (resolved instanceof PsiMethod && expression.getParent() instanceof PsiMethodCallExpression) {
        return processUsagesFlownDownTo(expression.getParent(), processor, builder);
      }
      if (!(resolved instanceof PsiVariable)) return true;
      // check for container item modifications, like "array[i] = expression;"
      addContainerReferences((PsiVariable)resolved, processor, builder);

      needToReportDeclaration = true;
      if (resolved instanceof PsiField || StackFilter.getElementContext(resolved) != StackFilter.getElementContext(expression)) {
        builder = builder.withFilter(JavaValueFilter::dropFrameFilter);
      }
      expression = resolved;
    }
    if (expression instanceof PsiRecordComponent) {
      PsiClass recordClass = ((PsiRecordComponent)expression).getContainingClass();
      if (recordClass != null) {
        int idx = ArrayUtil.indexOf(recordClass.getRecordComponents(), expression);
        PsiMethod constructor = JavaPsiRecordUtil.findCanonicalConstructor(recordClass);
        if (constructor != null) {
          PsiParameterList parameterList = constructor.getParameterList();
          PsiParameter parameter = parameterList.getParameter(idx);
          if (parameter != null) {
            if (!constructor.isPhysical()) {
              JavaSliceBuilder finalBuilder = builder;
              ReferencesSearch.search(recordClass, builder.getSearchScope()).forEach(ref -> {
                PsiElement refElement = ref.getElement();
                PsiNewExpression newExpression = ObjectUtils.tryCast(refElement.getParent(), PsiNewExpression.class);
                if (newExpression != null) {
                  processMethodCall(finalBuilder, processor, parameter.getType(), parameterList.getParameters(), idx, refElement);
                }
                return true;
              });
            } else {
              expression = parameter;
            }
          }
        }
      }
    }
    if (expression instanceof PsiVariable variable) {
      Collection<PsiExpression> values = DfaUtil.getVariableValues(variable, original);
      PsiExpression initializer = variable.getInitializer();
      if (values.isEmpty() && initializer != null) {
        values = Collections.singletonList(initializer);
      }
      boolean initializerReported = false;
      // no need to search and report assignments if we are going to report the variable anyway - it would create duplicate nodes if we did
      if (values.isEmpty() && !(variable instanceof PsiParameter) && !needToReportDeclaration) {
        values = DfaPsiUtil.getVariableAssignmentsInFile(variable, false, variable.getContainingFile().getLastChild());
        initializerReported = !values.isEmpty();
      }
      else if (!values.isEmpty() && !(variable instanceof PsiParameter)) {
        needToReportDeclaration = false; // already found all values
      }
      for (PsiExpression exp : values) {
        if (!builder.process(exp, processor)) return false;
        if (exp == initializer) initializerReported = true;
      }

      if (!initializerReported && needToReportDeclaration) { // otherwise we'll reach var declaration anyway because it is the initializer' parent
        // parameter or variable declaration can be far away from its usage (except for variable initializer) so report it separately
        return builder.process(variable, processor);
      }

      if (variable instanceof PsiField) {
        return processFieldUsages((PsiField)variable, builder.dropSyntheticField(), processor);
      }
      else if (variable instanceof PsiParameter) {
        return processParameterUsages((PsiParameter)variable, builder.withFilter(f -> f.popFrame(variable.getProject())), processor);
      }
    }
    if (expression instanceof PsiMethodCallExpression call) { // ctr call can't return value or be container get, so don't use PsiCall here
      PsiExpression returnedValue = JavaMethodContractUtil.findReturnedValue(call);
      if (returnedValue != null) {
        if (!builder.process(returnedValue, processor)) {
          return false;
        }
      }
      PsiMethod method = call.resolveMethod();
      Flow anno = method == null ? null : isMethodFlowAnnotated(method);
      if (anno != null) {
        String target = anno.target();
        if (target.equals(Flow.DEFAULT_TARGET)) target = Flow.RETURN_METHOD_TARGET;
        if (target.equals(Flow.RETURN_METHOD_TARGET)) {
          PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
          if (qualifier != null) {
            builder = builder.updateNesting(anno);
            String source = anno.source();
            if (source.equals(Flow.DEFAULT_SOURCE)) source = Flow.THIS_SOURCE;
            String synthetic = StringUtil.trimStart(StringUtil.trimStart(source, Flow.THIS_SOURCE),".");
            return processUsagesFlownDownTo(qualifier, processor, builder.withSyntheticField(synthetic));
          }
        }
      }
      if (method != null) {
        PsiParameter[] parameters = method.getParameterList().getParameters();
        boolean hasFlownParameter = false;
        for (int i = 0; i < parameters.length; i++) {
          Flow paramAnno = isParamFlowAnnotated(method, i);
          if (paramAnno != null && !parameters[i].isVarArgs()) {
            if (!builder.hasNesting() && !paramAnno.sourceIsContainer()) continue;
            PsiExpression[] args = call.getArgumentList().getExpressions();
            if (args.length > i) {
              JavaSliceBuilder argBuilder = builder.updateNesting(paramAnno);
              if (!argBuilder.process(args[i], processor)) {
                return false;
              }
              hasFlownParameter = true;
            }
          }
        }
        if (hasFlownParameter) return true;
      }
      return processMethodReturnValue(call, processor, builder);
    }
    if (expression instanceof PsiConditionalExpression conditional) {
      PsiExpression thenE = conditional.getThenExpression();
      PsiExpression elseE = conditional.getElseExpression();
      if (thenE != null && !builder.process(thenE, processor)) return false;
      if (elseE != null && !builder.process(elseE, processor)) return false;
    }
    if (expression instanceof PsiAssignmentExpression assignment) {
      IElementType tokenType = assignment.getOperationTokenType();
      PsiExpression rExpression = assignment.getRExpression();

      if (tokenType == JavaTokenType.EQ && rExpression != null) {
        return processUsagesFlownDownTo(rExpression, processor, builder);
      }
    }
    DfType filterDfType = builder.getFilter().getDfType();
    if (filterDfType != DfType.TOP && expression instanceof PsiExpression) {
      AnalysisStartingPoint analysis = AnalysisStartingPoint.propagateThroughExpression(expression, filterDfType);
      if (analysis != null) {
        return builder.withFilter(filter -> filter.withType(analysis.myDfType)).process(analysis.myAnchor, processor);
      }
    }

    if (builder.hasNesting()) {
      // consider container creation
      PsiElement initializer = expression instanceof PsiNewExpression ? ((PsiNewExpression)expression).getArrayInitializer() : expression;
      if (initializer instanceof PsiArrayInitializerExpression) {
        for (PsiExpression init : ((PsiArrayInitializerExpression)initializer).getInitializers()) {
          if (!builder.decrementNesting().process(init, processor)) return false;
        }
      }

      // check for constructor put arguments
      return !(expression instanceof PsiNewExpression) ||
             processContainerPutArguments((PsiNewExpression)expression, builder, processor);
    }
    return true;
  }
static boolean processUsagesFlownDownTo(@NotNull PsiElement expression,
                                          @NotNull Processor<? super SliceUsage> processor,
                                          @NotNull JavaSliceBuilder builder)