public ArrayList<Diagnostic> identify(SchemaNode node) {
        ArrayList<Diagnostic> ret = new ArrayList<Diagnostic>();

        if (node.isASTInstance(dataType.class)) {
            handleDataTypeDefinition(node, ret);
            return ret;
        }

        if (node.getLanguageType() == LanguageType.RANK_EXPRESSION) {
            return identifyDefinitionInRankExpression(node);
        }
        
        boolean isIdentifier = node.isASTInstance(identifierStr.class);
        boolean isIdentifierWithDash = node.isASTInstance(identifierWithDashStr.class);

        if (!isIdentifier && !isIdentifierWithDash) return ret;

        Node parent = node.getParent();
        if (parent == null) return ret;

        if (handleSpecialCases(node.getSchemaNode(), parent.getSchemaNode(), ret)) {
            return ret;
        }

        Map<Class<?>, SymbolType> searchMap = isIdentifier ? SchemaIndex.IDENTIFIER_TYPE_MAP : SchemaIndex.IDENTIFIER_WITH_DASH_TYPE_MAP;
        SymbolType symbolType = searchMap.get(parent.getASTClass());
        if (symbolType == null) return ret;

        // Root item, should not have a scope
        if (parent.isASTInstance(namedDocument.class) || parent.isASTInstance(rootSchema.class)) {
            node.setSymbol(symbolType, context.fileURI());
            node.setSymbolStatus(SymbolStatus.DEFINITION);
            context.schemaIndex().insertSymbolDefinition(node.getSymbol());
            return ret;
        }

        Optional<Symbol> scope = CSTUtils.findScope(node);
        if (scope.isEmpty()) {
            if (symbolType == SymbolType.RANK_PROFILE && parent.getParent() != null && parent.getParent().isASTInstance(RootRankProfile.class)) {
                // we are in a rank-profile file (.profile)
                String workspaceRootURI = context.scheduler().getWorkspaceURI();
                if (workspaceRootURI == null) return ret;
                String currentURI = context.fileURI();

                String schemaName = FileUtils.firstPathComponentAfterPrefix(currentURI, workspaceRootURI);

                if (schemaName == null) return ret;

                Optional<Symbol> schemaSymbol = context.schemaIndex().getSchemaDefinition(schemaName);

                if (schemaSymbol.isEmpty()) return ret;

                // TODO: rank-profile belonging to namedDocument??
                node.setSymbol(symbolType, context.fileURI(), schemaSymbol.get());
                node.setSymbolStatus(SymbolStatus.DEFINITION);
                context.schemaIndex().insertSymbolDefinition(node.getSymbol());
            }
            return ret;
        }

        node.setSymbol(symbolType, context.fileURI(), scope.get());

        // Check if this is an invalid 'redefinition' of existing identifier.
        Optional<Symbol> existingSymbol = context.schemaIndex().findSymbolInScope(node.getSymbol());

        if (existingSymbol.isEmpty()) {
            node.setSymbolStatus(SymbolStatus.DEFINITION);
            context.schemaIndex().insertSymbolDefinition(node.getSymbol());

            if (node.getSymbol().getType() == SymbolType.FUNCTION) {
                verifySymbolFunctionName(node, ret);
            }
            return ret;
        } 

        node.setSymbolStatus(SymbolStatus.INVALID);

        if (symbolType == SymbolType.FIELD) {
            Range range = null;

            if (parent.getParent().isASTInstance(fieldOutsideDoc.class)) {
                range = node.getRange();
            } else if (!context.fieldIndex().getIsInsideDoc(existingSymbol.get())) {
                range = existingSymbol.get().getNode().getRange();
            }

            if (range != null)
                ret.add(new SchemaDiagnostic.Builder()
                    .setRange(range)
                    .setMessage("Field '" + node.getText() + "' shadows a document field with the same name.")
                    .setSeverity(DiagnosticSeverity.Warning)
                    .build());
        }

        return ret;
    }
Marks the node as a symbol with SymbolStatus DEFINITION
It is mainly based on the node type being an identifier, and the parent being of a certain type.
But in a lot of cases we need to check more.
integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/parser/schema/IdentifySymbolDefinition.java