diff --git a/crates/ecp-analyzer/src/post_process/schema_field_mirrors.rs b/crates/ecp-analyzer/src/post_process/schema_field_mirrors.rs index b9e015b56..7a0f0576e 100644 --- a/crates/ecp-analyzer/src/post_process/schema_field_mirrors.rs +++ b/crates/ecp-analyzer/src/post_process/schema_field_mirrors.rs @@ -40,6 +40,7 @@ use crate::resolution::index::SymbolTable; use ecp_core::analyzer::types::{LocalGraph, SchemaType}; use ecp_core::graph::{Edge, Node, NodeKind, RelType}; use ecp_core::pool::StringPool; +use ecp_core::uid; use rustc_hash::FxHashMap; /// Promote `RawSchemaField`s to `SchemaField` Nodes + emit `HasProperty` @@ -96,23 +97,28 @@ pub fn emit_edges( continue; }; - // UID format: stable across reindex; includes framework so - // cross-framework mirrors with same (owner, name) get distinct - // UIDs. Format mirrors File-node UID convention. - let uid_str = format!( - "SchemaField:{:?}:{}.{}:{}", - raw_sf.framework, owner_name, field_name, path_str + // UID: T1-5 canonical xxh3-64 over (kind, path, owner, name). + // owner_class is included so cross-framework mirrors with the same + // (owner, name) but different SchemaField nodes get distinct UIDs + // via path / owner disambiguation upstream. + let node_uid = uid::compute( + NodeKind::SchemaField, + &path_str, + Some(owner_name), + field_name, ); - let uid_ref = string_pool.add(&uid_str); let name_ref = string_pool.add(field_name); + let owner_ref = string_pool.add(owner_name); let sf_idx = nodes.len() as u32; nodes.push(Node { - uid: uid_ref, + uid: node_uid, name: name_ref, file_idx, kind: NodeKind::SchemaField, span: raw_sf.span, community_id: 0, + owner_class: owner_ref, + content_hash: 0, }); // HasProperty: -> . Non-heuristic diff --git a/crates/ecp-analyzer/src/protobuf/parser.rs b/crates/ecp-analyzer/src/protobuf/parser.rs index 502bc5afe..6bc6fe3e9 100644 --- a/crates/ecp-analyzer/src/protobuf/parser.rs +++ b/crates/ecp-analyzer/src/protobuf/parser.rs @@ -8,7 +8,6 @@ use super::schema_extractors::{ }; use ecp_core::analyzer::provider::LanguageProvider; use ecp_core::analyzer::types::{LocalGraph, RawSchemaField}; -use ecp_core::pool::StringPool; use std::path::Path; pub struct ProtobufProvider; @@ -28,8 +27,7 @@ impl LanguageProvider for ProtobufProvider { let text = std::str::from_utf8(source) .map_err(|e| anyhow::anyhow!("protobuf: UTF-8 decode error in {:?}: {}", path, e))?; - let mut pool = StringPool::new(); - let fields = extract_proto_fields(text, &mut pool); + let fields = extract_proto_fields(text); let schema_fields = (!fields.is_empty()).then(|| fields.into_boxed_slice()); Ok(LocalGraph { @@ -48,7 +46,7 @@ impl LanguageProvider for ProtobufProvider { /// - `depth`: brace nesting depth. A top-level `message` bumps depth to 1; /// any nested `{` (including nested messages, oneofs, options) bumps it /// further. Fields are only emitted when `depth == 1`. -fn extract_proto_fields(text: &str, pool: &mut StringPool) -> Vec { +fn extract_proto_fields(text: &str) -> Vec { let mut out: Vec = Vec::new(); let mut current_message: Option = None; let mut depth: u32 = 0; @@ -102,9 +100,9 @@ fn extract_proto_fields(text: &str, pool: &mut StringPool) -> Vec