private Set<Function> getCallers(SdFunctionDefinition function, Map<String, List<Function>> functionsMap) {
        String functionName = function.getName();
        if (functionTreeChildren.containsKey(functionName)) {
            return functionTreeChildren.get(functionName);
        }
        
        Set<Function> results = new HashSet<>();
        for (Function functionImpl : functionsMap.get(functionName)) {
            SearchScope searchScope = getSearchScope(myScopeType, functionImpl.definition());
            ReferencesSearch.search(functionImpl.definition(), searchScope).forEach((Consumer<? super PsiReference>) r -> {
                ProgressManager.checkCanceled();
                PsiElement psiElement = r.getElement();
                SdFunctionDefinition f = PsiTreeUtil.getParentOfType(psiElement, SdFunctionDefinition.class, false);
                if (f != null && f.getName() != null && !f.getName().equals(functionName)) {
                    results.add(Function.from(f, null));
                } else {
                    SdFirstPhaseDefinition fp = PsiTreeUtil.getParentOfType(psiElement, SdFirstPhaseDefinition.class, false);
                    if (fp != null)
                        results.add(Function.from(fp, null));
                    else {
                        SdSecondPhaseDefinition sp = PsiTreeUtil.getParentOfType(psiElement, SdSecondPhaseDefinition.class, false);
                        if (sp != null)
                            results.add(Function.from(sp, null));
                    }
                }
            });
        }
        
        functionTreeChildren.put(functionName, results);
        return results;
    }
A Caller tree in the "Call Hierarchy" window.

@author Shahar Ariel
integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallerTreeStructure.java