use crate::domain::{RepositoryCodeRange, RepositoryCodeReferenceRecord};
use super::*;
#[test]
fn same_path_overloads_remain_ambiguous_even_when_one_contains_call_line() {
let mut symbols = vec![
symbol("sym-int", "src/overload.cpp", "run"),
symbol("sym-double", "src/overload.cpp", "run"),
];
symbols[0].line_range = RepositoryCodeRange { start: 1, end: 3 };
symbols[1].line_range = RepositoryCodeRange { start: 5, end: 5 };
let mut references = vec![reference("ref-call", "src/overload.cpp", "run")];
references[0].line_range = RepositoryCodeRange { start: 2, end: 2 };
resolve_reference_targets(&symbols, &mut references);
assert_eq!(references[0].target_symbol_snapshot_id, None);
assert_eq!(references[0].resolution_state, "ambiguous");
}
#[test]
fn call_resolution_prefers_implementation_over_header_declaration() {
let mut symbols = vec![
symbol("header-declaration", "include/rk_bridge.h", "rk_c_decode"),
symbol("source-definition", "src/c_entry.c", "rk_c_decode"),
];
symbols[0].kind = "function_declaration".to_owned();
let mut references = vec![reference("cpp-call", "src/cpp_bridge.cpp", "rk_c_decode")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("source-definition")
);
assert_eq!(references[0].target_hint.as_deref(), Some("rk_c_decode"));
assert_eq!(references[0].resolution_state, "resolved");
}
#[test]
fn call_resolution_follows_cgo_and_ffi_aliases_to_c_symbols() {
let symbols = vec![symbol("c-definition", "src/c_entry.c", "rk_c_decode")];
let mut references = vec![
reference("go-cgo-call", "bridge/go_bridge.go", "C.rk_c_decode"),
reference(
"rust-ffi-call",
"crates/rust_bridge/src/lib.rs",
"ffi::rk_c_decode",
),
];
resolve_reference_targets(&symbols, &mut references);
assert!(references.iter().all(|reference| {
reference.target_symbol_snapshot_id.as_deref() == Some("c-definition")
&& reference.resolution_state == "resolved"
}));
assert_eq!(references[0].target_hint.as_deref(), Some("C.rk_c_decode"));
assert_eq!(
references[1].target_hint.as_deref(),
Some("ffi::rk_c_decode")
);
}
#[test]
fn call_resolution_does_not_leaf_alias_ordinary_namespaced_calls() {
let symbols = vec![symbol("plain-connect", "src/socket.c", "connect")];
let mut references = vec![
reference(
"rust-module-call",
"crates/app/src/lib.rs",
"module::connect",
),
reference(
"rust-module-sys-call",
"crates/app/src/lib.rs",
"module::sys::connect",
),
reference("rust-c-member-call", "crates/app/src/lib.rs", "C.connect"),
reference("go-object-c-call", "bridge/go_bridge.go", "obj.C.connect"),
];
resolve_reference_targets(&symbols, &mut references);
assert!(references.iter().all(|reference| {
reference.target_symbol_snapshot_id.is_none() && reference.resolution_state == "unresolved"
}));
assert_eq!(
references[0].target_hint.as_deref(),
Some("module::connect")
);
assert_eq!(
references[1].target_hint.as_deref(),
Some("module::sys::connect")
);
assert_eq!(references[2].target_hint.as_deref(), Some("C.connect"));
assert_eq!(references[3].target_hint.as_deref(), Some("obj.C.connect"));
}
#[test]
fn call_resolution_ignores_unique_non_callable_leaf_targets() {
let mut symbols = vec![symbol("constant-connect", "src/constants.rs", "connect")];
symbols[0].kind = "constant".to_owned();
let mut references = vec![reference("ffi-connect", "src/lib.rs", "ffi::connect")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(references[0].target_symbol_snapshot_id, None);
assert_eq!(references[0].target_hint.as_deref(), Some("ffi::connect"));
assert_eq!(references[0].resolution_state, "unresolved");
}
#[test]
fn dependency_references_can_resolve_to_module_symbols() {
let mut symbols = vec![symbol("go-module", "libs/lib/go.mod", "example.com/lib")];
symbols[0].kind = "module".to_owned();
let mut references = vec![reference("go-require", "go.mod", "example.com/lib")];
references[0].kind = "dependency".to_owned();
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("go-module")
);
assert_eq!(references[0].resolution_state, "resolved");
}
#[test]
fn call_resolution_continues_to_leaf_after_scoped_ambiguity() {
let mut symbols = vec![
symbol("ffi-declaration-a", "src/bindings_a.rs", "ffi::rk_c_decode"),
symbol("ffi-declaration-b", "src/bindings_b.rs", "ffi::rk_c_decode"),
symbol("c-definition", "src/c_entry.c", "rk_c_decode"),
];
symbols[0].kind = "function_declaration".to_owned();
symbols[1].kind = "function_declaration".to_owned();
let mut references = vec![reference("ffi-call", "src/lib.rs", "ffi::rk_c_decode")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("c-definition")
);
assert_eq!(
references[0].target_hint.as_deref(),
Some("ffi::rk_c_decode")
);
assert_eq!(references[0].resolution_state, "resolved");
}
#[test]
fn call_resolution_prefers_leaf_definition_over_unique_scoped_declaration() {
let mut symbols = vec![
symbol("ffi-declaration", "src/bindings.rs", "ffi::rk_c_decode"),
symbol("c-definition", "src/c_entry.c", "rk_c_decode"),
];
symbols[0].kind = "function_declaration".to_owned();
let mut references = vec![reference("ffi-call", "src/lib.rs", "ffi::rk_c_decode")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("c-definition")
);
assert_eq!(
references[0].target_hint.as_deref(),
Some("ffi::rk_c_decode")
);
assert_eq!(references[0].resolution_state, "resolved");
}
#[test]
fn call_resolution_treats_signature_only_functions_as_declarations() {
let mut symbols = vec![
symbol("ffi-declaration", "src/bindings.rs", "ffi::rk_c_decode"),
symbol("c-definition", "src/c_entry.c", "rk_c_decode"),
];
symbols[0].signature = "fn rk_c_decode(input: *const u8);".to_owned();
let mut references = vec![reference("ffi-call", "src/lib.rs", "ffi::rk_c_decode")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("c-definition")
);
assert_eq!(
references[0].target_hint.as_deref(),
Some("ffi::rk_c_decode")
);
assert_eq!(references[0].resolution_state, "resolved");
}
#[test]
fn call_resolution_defers_ambiguous_declaration_until_leaf_fallback() {
let mut symbols = vec![
symbol("ffi-declaration", "src/bindings.rs", "ffi::rk_c_decode"),
symbol("ffi-constant", "src/constants.rs", "ffi::rk_c_decode"),
symbol("c-definition", "src/c_entry.c", "rk_c_decode"),
];
symbols[0].kind = "function_declaration".to_owned();
symbols[1].kind = "constant".to_owned();
let mut references = vec![reference("ffi-call", "src/lib.rs", "ffi::rk_c_decode")];
resolve_reference_targets(&symbols, &mut references);
assert_eq!(
references[0].target_symbol_snapshot_id.as_deref(),
Some("c-definition")
);
assert_eq!(
references[0].target_hint.as_deref(),
Some("ffi::rk_c_decode")
);
assert_eq!(references[0].resolution_state, "resolved");
}
fn symbol(id: &str, path: &str, name: &str) -> RepositoryCodeSymbolRecord {
RepositoryCodeSymbolRecord {
repository_id: "repo".to_owned(),
source_scope: "git_snapshot:test".to_owned(),
symbol_snapshot_id: id.to_owned(),
canonical_symbol_id: format!("repo://repo/{}::{name}", path.replace('/', "::")),
file_id: format!("file-{id}"),
path: path.to_owned(),
language_id: "cpp".to_owned(),
name: name.to_owned(),
qualified_name: format!("{}::{name}", path.replace('/', "::")),
kind: "function".to_owned(),
signature: format!("void {name}()"),
doc_comment: None,
byte_range: RepositoryCodeRange { start: 0, end: 8 },
line_range: RepositoryCodeRange { start: 1, end: 1 },
symbol_role: None,
}
}
fn reference(id: &str, path: &str, name: &str) -> RepositoryCodeReferenceRecord {
RepositoryCodeReferenceRecord {
repository_id: "repo".to_owned(),
source_scope: "git_snapshot:test".to_owned(),
reference_id: id.to_owned(),
file_id: format!("file-{id}"),
path: path.to_owned(),
name: name.to_owned(),
kind: "call".to_owned(),
target_symbol_snapshot_id: None,
target_hint: Some(name.to_owned()),
resolution_state: "unresolved".to_owned(),
confidence_basis_points: 2_500,
confidence_tier: "ambiguous".to_owned(),
byte_range: RepositoryCodeRange { start: 0, end: 8 },
line_range: RepositoryCodeRange { start: 1, end: 1 },
}
}