public static void resolveSymbolReference(SchemaNode node, ParseContext context, List<Diagnostic> diagnostics) {
        Optional<Symbol> referencedSymbol = Optional.empty();
        // dataType is handled separately
        SymbolType referencedType = node.getSymbol().getType();

        // Some symbol types require special handling
        if (referencedType == SymbolType.SUBFIELD) {
            Optional<Symbol> parentFieldDefinition = Optional.empty();

            // Two cases for where the parent field is defined. Either inside a struct or "global".
            if (node.getLanguageType() == LanguageType.RANK_EXPRESSION) {
                Node outNode = node.getParent();
                Node parentNode = outNode.getParent();

                int outNodeIndex = parentNode.indexOf(outNode);
                if (outNodeIndex == 0) {
                    Node fieldDefinitionNode = parentNode.getSibling(-2);
                    if (fieldDefinitionNode != null && fieldDefinitionNode.hasSymbol()) {
                        parentFieldDefinition = context.schemaIndex().getSymbolDefinition(fieldDefinitionNode.getSymbol());
                    }
                } else {
                    Node previousOutNode = parentNode.get(outNodeIndex - 2);
                    if (previousOutNode != null && previousOutNode.size() > 0 && previousOutNode.get(0).hasSymbol()) {
                        parentFieldDefinition = context.schemaIndex().getSymbolDefinition(previousOutNode.get(0).getSymbol());
                    }
                }

            } else {

                Node parentField = node.getPreviousSibling();
                if (parentField.getASTClass() == DOT.class) parentField = parentField.getPreviousSibling();

                if (parentField.hasSymbol() && parentField.getSymbol().getStatus() == SymbolStatus.REFERENCE) {
                    parentFieldDefinition = context.schemaIndex().getSymbolDefinition(parentField.getSymbol());
                }
            }
            

            if (parentFieldDefinition.isPresent()) {
                referencedSymbol = resolveSubFieldReference(node, parentFieldDefinition.get(), context, diagnostics);
            }
        } else if (referencedType == SymbolType.FUNCTION) {
            referencedSymbol = context.schemaIndex().findSymbol(node.getSymbol().getScope(), SymbolType.FUNCTION, node.getSymbol().getShortIdentifier());

            if (referencedSymbol.isEmpty()) {
                // could be built in
                SchemaNode parent = node.getParent().getSchemaNode();

                if ((parent.getOriginalRankExpressionNode() instanceof BaseNode) && ((BaseNode)parent.getOriginalRankExpressionNode()).expressionNode instanceof FunctionNode) {
                    node.setSymbolStatus(SymbolStatus.BUILTIN_REFERENCE);
                    return;
                }

                if (BuiltInFunctions.simpleBuiltInFunctionsSet.contains(node.getSymbol().getShortIdentifier())) {
                    node.setSymbolStatus(SymbolStatus.BUILTIN_REFERENCE);
                    return;
                }
                //if (BuiltInFunctions.rankExpressionBultInFunctions.get(identifier) != null) {
                //    node.setSymbolStatus(SymbolStatus.BUILTIN_REFERENCE);
                //}
            }
        } else if (referencedType == SymbolType.TYPE_UNKNOWN) {

            if (didExpectLabel(node, diagnostics)) {
                node.setSymbolType(SymbolType.LABEL);
                node.setSymbolStatus(SymbolStatus.BUILTIN_REFERENCE);
                return;
            }

            // These roughly go from narrow to broad scope.
            SymbolType[] possibleTypes = new SymbolType[] { SymbolType.PARAMETER, SymbolType.FUNCTION, SymbolType.RANK_CONSTANT, SymbolType.FIELD };

            for (SymbolType type : possibleTypes) {
                referencedSymbol = context.schemaIndex().findSymbol(node.getSymbol().getScope(), type, node.getSymbol().getShortIdentifier());
                if (referencedSymbol.isPresent()) {
                    node.setSymbolType(type);
                    break;
                }
            }

            if (referencedSymbol.isEmpty() && BuiltInFunctions.simpleBuiltInFunctionsSet.contains(node.getSymbol().getShortIdentifier())) {
                node.setSymbolType(SymbolType.FUNCTION);
                node.setSymbolStatus(SymbolStatus.BUILTIN_REFERENCE);
                return;
            }

        } else if (
            referencedType == SymbolType.RANK_PROFILE 
         && (node.getParent().isASTInstance(summaryFeaturesElm.class) || node.getParent().isASTInstance(matchFeaturesElm.class))
         && node.getSymbol().getScope() != null 
         && node.getSymbol().getScope().getType() == SymbolType.RANK_PROFILE) {
            // This happens if you write i.e. summary-features inherits some_profile
            // See RankProfile.java in config-model com.yahoo.schema
            var rankProfileGraph = context.schemaIndex().getRankProfileInheritanceGraph();

            String myIdentifier = node.getSymbol().getShortIdentifier();
            Symbol myRankProfileDefinitionSymbol = node.getSymbol().getScope();

            var result = rankProfileGraph.getAllParents(myRankProfileDefinitionSymbol)
                .stream()
                .filter(symbol -> symbol.getShortIdentifier().equals(myIdentifier))
                .toList();

            if (!result.isEmpty()) {
                if (result.size() > 1) {
                    // This can most likely never happen, but if it does it should be warned
                    diagnostics.add(new SchemaDiagnostic.Builder()
                        .setRange(node.getRange())
                        .setMessage(myIdentifier + " is ambiguous in this context")
                        .setSeverity(DiagnosticSeverity.Warning)
                        .build());
                }
                referencedSymbol = Optional.of(result.get(0));
            } else {
                // We can try to find a rank-profile in case the user forgot to inherit the rank profile as well
                // We will in that case assign it to referencedSymbol so go-to-definition and stuff still works
                referencedSymbol = context.schemaIndex().findSymbol(node.getSymbol());

                if (referencedSymbol.isPresent()) {
                    node.getSymbol().setStatus(SymbolStatus.INVALID);
                    String constructType = node.getPreviousSibling().getPreviousSibling().getText(); // i.e. "summary-features"
                    diagnostics.add(new SchemaDiagnostic.Builder()
                        .setRange(node.getRange())
                        .setMessage("This can only inherit the " + constructType + " of a directly inherited profile.")
                        .setSeverity(DiagnosticSeverity.Error)
                        .setCode(DiagnosticCode.FEATURES_INHERITS_NON_PARENT)
                        .build()
                    );
                }

            }
        } else {
            referencedSymbol = context.schemaIndex().findSymbol(node.getSymbol());
        }

        if (node.getLanguageType() == LanguageType.RANK_EXPRESSION && referencedSymbol.isEmpty()) {
            referencedSymbol = context.schemaIndex().findSymbol(node.getSymbol(), SymbolType.PARAMETER, node.getSymbol().getShortIdentifier());
        }

        if (referencedSymbol.isPresent()) {
            node.setSymbolStatus(SymbolStatus.REFERENCE);
            context.schemaIndex().insertSymbolReference(referencedSymbol.get(), node.getSymbol());

        } else if (referencedType != SymbolType.QUERY_INPUT)  {
            context.schemaIndex().addUnresolvedSymbol(node.getSymbol());
            diagnostics.add(new SchemaDiagnostic.Builder()
                    .setRange( node.getRange())
                    .setMessage( "Undefined symbol " + node.getText())
                    .setSeverity(DiagnosticSeverity.Error)
                    .setCode(DiagnosticCode.UNDEFINED_SYMBOL)
                    .build() );
        }
    }
SymbolReferenceResolver goes through the unresolved symbol references and searches for their definition and marks the symbols as reference.
If no definition was found a error is sent to the client.

Must run after RankExpressionSymbolResolver
integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/SymbolReferenceResolver.java