private ArrayList<Diagnostic> identifyRankExpressionLanguage(SchemaNode node) {
        ArrayList<Diagnostic> ret = new ArrayList<>();
        if (!identifierNodes.contains(node.getOriginalRankExpressionNode().getClass())) return ret;

        SchemaNode parent = node.getParent().getSchemaNode();


        if (!(parent.getOriginalRankExpressionNode() instanceof BaseNode)) return ret;

        ExpressionNode expressionNode = ((BaseNode)parent.getOriginalRankExpressionNode()).expressionNode;

        if (expressionNode instanceof ReferenceNode) {

            SymbolType type = SymbolType.TYPE_UNKNOWN;

            Reference reference = ((ReferenceNode)expressionNode).reference();
            String referenceTo = reference.name();

            if (!reference.isIdentifier())type = SymbolType.FUNCTION;


            Optional<Symbol> scope = CSTUtils.findScope(node);

            if (referenceTo.equals("file")) {
                // TODO: handle expression files;
                return ret;
            }

            if (scope.isPresent()) {
                node.setSymbol(type, context.fileURI(), scope.get(), referenceTo);
            } else {
                node.setSymbol(type, context.fileURI(), null, referenceTo);
            }
            node.setSymbolStatus(SymbolStatus.UNRESOLVED);
        } else if (parent.getOriginalRankExpressionNode() instanceof BaseNode 
            && ((BaseNode)parent.getOriginalRankExpressionNode()).expressionNode instanceof FunctionNode) {
            // This might be a function reference
            Optional<Symbol> scope = CSTUtils.findScope(node);
            if (scope.isPresent()) {
                node.setSymbol(SymbolType.FUNCTION, context.fileURI(), scope.get(), node.getText());
            } else {
                node.setSymbol(SymbolType.FUNCTION, context.fileURI(), null, node.getText());
            }
            node.setSymbolStatus(SymbolStatus.UNRESOLVED);
        } else if (parent.getIsDirty() && node.getNextSibling().isASTInstance(LBRACE.class) && node.getNextSibling().getNextSibling().isASTInstance(args.class)) {
            // This case happens if you write feature(). (with nothing after the dot). 
            // Parser marks the parent as dirty, so it doesnt receive a ReferenceNode
            Optional<Symbol> scope = CSTUtils.findScope(node);

            if (scope.isPresent()) {
                node.setSymbol(SymbolType.FUNCTION, context.fileURI(), scope.get());
            } else {
                node.setSymbol(SymbolType.FUNCTION, context.fileURI());
            }
        }
        return ret;
    }
If the parent of an identifier belongs to one of these classes
it will be handled by the {@link IdentifySymbolReferences#handleFieldReference} method.

This involves splitting the identifierStr node into several nodes based on the dot-syntax for fields.
integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/parser/schema/IdentifySymbolReferences.java