boolean (@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) { 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); } PsiReferenceExpression ref = (PsiReferenceExpression)expression; 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; }