public static Optional<Symbol> resolveSubFieldReference(SchemaNode node, Symbol fieldDefinition, ParseContext context, List<Diagnostic> diagnostics) {
        if (fieldDefinition.getType() != SymbolType.FIELD 
            && fieldDefinition.getType() != SymbolType.MAP_VALUE
            && fieldDefinition.getType() != SymbolType.STRUCT) return Optional.empty();
        if (fieldDefinition.getStatus() != SymbolStatus.DEFINITION) return Optional.empty();

        if (fieldDefinition.getType() == SymbolType.STRUCT) {
            return resolveFieldInStructReference(node, fieldDefinition, context);
        }

        // First check for struct-field definitions
        if (fieldDefinition.getType() == SymbolType.FIELD) {
            Optional<Symbol> structFieldDefinition = context.schemaIndex().findSymbolInScope(fieldDefinition, SymbolType.FIELD, node.getText());
            if (structFieldDefinition.isPresent()) {
                node.setSymbolType(SymbolType.FIELD);
                return structFieldDefinition;
            }
        }

        SchemaNode dataTypeNode = null;
        Optional<Symbol> referencedSymbol = Optional.empty();
        if (fieldDefinition.getType() == SymbolType.MAP_VALUE) {
            dataTypeNode = fieldDefinition.getNode().getSchemaNode();
        } else if (fieldDefinition.getType() == SymbolType.FIELD) {
            if (fieldDefinition.getNode().getNextSibling() == null || fieldDefinition.getNode().getNextSibling().getNextSibling() == null) return Optional.empty();
            dataTypeNode = fieldDefinition.getNode().getNextSibling().getNextSibling().getSchemaNode();
            if (!(dataTypeNode.getASTClass() == dataType.class)) return Optional.empty();


            if (dataTypeNode.hasSymbol()) {
                // TODO: handle annotation reference and document reference?
                if (!isStructReference(dataTypeNode)) return Optional.empty();

                Symbol structReference = dataTypeNode.getSymbol();
                Symbol structDefinition = context.schemaIndex().getSymbolDefinition(structReference).get();
                return resolveFieldInStructReference(node, structDefinition, context);
            } else if (dataTypeNode.get(0).getASTClass() == REFERENCE.class) {
                if (dataTypeNode.size() < 3 || !dataTypeNode.get(2).get(0).hasSymbol()) return Optional.empty();
                Symbol documentReference = dataTypeNode.get(2).get(0).getSymbol();

                Optional<Symbol> documentDefinition = Optional.empty();

                if (documentReference.getStatus() == SymbolStatus.REFERENCE) {
                    documentDefinition = context.schemaIndex().getSymbolDefinition(documentReference);
                } else {
                    documentDefinition = context.schemaIndex().findSymbol(documentReference);
                }

                if (documentDefinition.isEmpty()) return Optional.empty();

                referencedSymbol = context.schemaIndex().findSymbol(documentDefinition.get(), SymbolType.FIELD, node.getText());

                if (referencedSymbol.isPresent()) {
                    node.setSymbolType(referencedSymbol.get().getType());
                    var referencedSymbolIndexingTypes = context.fieldIndex().getFieldIndexingTypes(referencedSymbol.get());

                    // We identified the reference and found the definition,
                    // however this case is actually only valid in an import field statement.
                    // So we should add an error if thats not the case
                    if (node.getParent().getASTClass() != importField.class) {
                        diagnostics.add(new SchemaDiagnostic.Builder()
                                .setRange( node.getRange())
                                .setMessage( "Field " + referencedSymbol.get().getLongIdentifier() + " can not be accessed directly. Hint: Add an import field statement to access the field.")
                                .setSeverity( DiagnosticSeverity.Error)
                                .setCode(DiagnosticCode.ACCESS_UNIMPORTED_FIELD)
                                .build());
                        return referencedSymbol;
                    }
                    if (!referencedSymbolIndexingTypes.contains(IndexingType.ATTRIBUTE)) {
                        diagnostics.add(new SchemaDiagnostic.Builder()
                                .setRange( node.getRange())
                                .setMessage( "Cannot import " + referencedSymbol.get().getLongIdentifier() + " because it is not an attribute field. Only attribute fields can be imported.")
                                .setSeverity( DiagnosticSeverity.Error)
                                .setCode(DiagnosticCode.IMPORT_FIELD_ATTRIBUTE)
                                .build() );
                    } else if (referencedSymbolIndexingTypes.contains(IndexingType.INDEX)) {
                        // TODO: quickfix
                        diagnostics.add(new SchemaDiagnostic.Builder()
                                .setRange( node.getRange())
                                .setMessage( "Cannot import " + referencedSymbol.get().getLongIdentifier() + " because it is an index field. Importing index fields is not supported.")
                                .setSeverity( DiagnosticSeverity.Error)
                                .build() );
                    }

                    Symbol importFieldDefinitionSymbol = node.getNextSibling().getNextSibling().getSymbol();
                    if (importFieldDefinitionSymbol != null && importFieldDefinitionSymbol.getStatus() == SymbolStatus.DEFINITION) {
                        for (IndexingType indexingType : referencedSymbolIndexingTypes) {
                            context.fieldIndex().addFieldIndexingType(importFieldDefinitionSymbol, indexingType);
                        }
                    }
                }

                return referencedSymbol;
            }
        } else {
            return Optional.empty();
        }


        dataType originalNode = (dataType)dataTypeNode.getOriginalSchemaNode();
        if (originalNode.getParsedType().getVariant() == Variant.MAP) {
            return resolveMapValueReference(node, fieldDefinition, context);
        } else if (originalNode.getParsedType().getVariant() == Variant.ARRAY) {
            if (dataTypeNode.size() < 3 || dataTypeNode.get(2).getASTClass() != dataType.class) return Optional.empty();

            Node innerType = dataTypeNode.get(2);
            if (!isStructReference(innerType)) return Optional.empty();

            Symbol structReference = innerType.getSymbol();
            Symbol structDefinition = context.schemaIndex().getSymbolDefinition(structReference).get();

            return resolveFieldInStructReference(node, structDefinition, context);
        }
        return referencedSymbol;
    }
This finds the definition of a field inside a struct, where the struct is used as a type for some parent field
It solves the case where we have 
struct foo { field bar }
field baz type foo {}
And try to access baz.bar

In this case baz is the parent field and bar is the subfield

@param node the node pointing to the subfield declaration 
@param fieldDefinition the symbol where the parent field is *defined*
@param context 

@return the definition of the field inside the struct if found
integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/SymbolReferenceResolver.java