mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-02 09:45:39 +08:00
* fix(extraction): record callers for methods passed as values (#1820) pool.submit(obj.method), thread_pool_exec(self.store.fetch), and Go Submit(c.store.Fetch) produced no callers/impact edge because only obj.method(...) call expressions were captured. Member values are now emitted as *.method function-refs and resolve unique-or-drop (same-file first). Two methods of the same name still produce no edge. Go go c.store.Fetch(ids) remains a calls edge (it is a call expression). EXTRACTION_VERSION 27: MCP daemon catch-up rebuilds a stale index on start so the watcher does not keep serving the hole after upgrade. * fix(resolution): do not let test mocks veto #1820 method-value callers A unique production method plus a unit-test double of the same name was treated as ambiguous, so retrieve_vision_vectors still looked callback-invisible on a real repo. Prefer the non-test file when exactly one production target remains. * fix(resolution): prefer a real override over a NotImplementedError base #1820 unique-or-drop treated DocStoreConnection.delete_payload_fields (the ABC stub) and QdrantConnection.delete_payload_fields as ambiguous, so gc_stale_member_stamps stayed invisible. Skip short not-implemented stubs when exactly one real implementation remains. * fix(extraction): preserve receivers for method values (#1820) Python attributes and Go selectors used as callback values were dropped or reduced to bare method names. Preserve receiver paths in native and WASM extraction, resolve through type/import scope, and reject ambiguous or noncallable targets. Cover callback, assignment, collection, and query behavior, with extraction version 27 signaling the required reindex. Co-authored-by: Josh66 <info@aska-systems.de> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Josh66 <info@aska-systems.de> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Josh66
Claude Opus 5.5
parent
2a2f71ec41
commit
5eaa6fedd5
@@ -1009,6 +1009,31 @@ impl<'t> Walker<'t> {
|
||||
row: p.row,
|
||||
});
|
||||
}
|
||||
// #1820: preserve the receiver of a method value.
|
||||
"selector_expression" => {
|
||||
let field = v
|
||||
.child_by_field_name("field")
|
||||
.or_else(|| v.named_child(v.named_child_count().saturating_sub(1)));
|
||||
let Some(field) = field else { return };
|
||||
let name = self.text(field);
|
||||
if name.is_empty() || is_stoplisted(name) {
|
||||
return;
|
||||
}
|
||||
let value = self.text(v);
|
||||
if !value.split('.').all(|part| {
|
||||
!part.is_empty() && part.chars().enumerate().all(|(i, c)| {
|
||||
c == '_' || c.is_ascii_alphabetic() || (i > 0 && c.is_ascii_digit())
|
||||
})
|
||||
}) { return; }
|
||||
let p = field.start_position();
|
||||
self.fn_ref_cands.push(Cand {
|
||||
from,
|
||||
name: value.to_string(),
|
||||
line: p.row as u32 + 1,
|
||||
column_byte: field.start_byte(),
|
||||
row: p.row,
|
||||
});
|
||||
}
|
||||
"literal_element" | "expression_list" => {
|
||||
for i in 0..v.named_child_count() {
|
||||
if let Some(c) = v.named_child(i) {
|
||||
@@ -1050,6 +1075,7 @@ impl<'t> Walker<'t> {
|
||||
let mut seen: HashSet<(String, String)> = HashSet::new();
|
||||
for c in cands {
|
||||
if !c.name.starts_with("this.")
|
||||
&& !c.name.contains('.')
|
||||
&& !c.name.contains("::")
|
||||
&& !self.defined_fn_names.contains(&c.name)
|
||||
&& !self.imported_names.contains(&c.name)
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
//! decorators (`@staticmethod` yes, `@app.route(...)` no — python's `call`
|
||||
//! kind isn't `call_expression`), module-level assignments always extract as
|
||||
//! `variable` (no isConst hook), and `self.method` fn-ref candidates carry the
|
||||
//! BARE attribute name. Python is not a TYPE_ANNOTATION language — no type
|
||||
//! full receiver path (#1820).
|
||||
//! Python is not a TYPE_ANNOTATION language — no type
|
||||
//! refs anywhere. Files with parse errors defer to wasm.
|
||||
|
||||
use crate::buffers::{
|
||||
@@ -903,18 +904,16 @@ impl<'t> Walker<'t> {
|
||||
for v in values {
|
||||
let (name, anchor) = match v.kind() {
|
||||
"identifier" => (self.text(v).to_string(), v),
|
||||
// `self.handle_click` — object EXACTLY `self`; BARE attr name.
|
||||
// Preserve only statically named member chains, as on WASM.
|
||||
"attribute" => {
|
||||
let obj = v.child_by_field_name("object");
|
||||
let attr = v.child_by_field_name("attribute");
|
||||
match (obj, attr) {
|
||||
(Some(o), Some(a))
|
||||
if o.kind() == "identifier" && self.text(o) == "self" =>
|
||||
{
|
||||
(self.text(a).to_string(), a)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
let Some(attr) = v.child_by_field_name("attribute") else { continue };
|
||||
let name = self.text(v);
|
||||
if !name.split('.').all(|part| {
|
||||
!part.is_empty() && part.chars().enumerate().all(|(i, c)| {
|
||||
c == '_' || c.is_ascii_alphabetic() || (i > 0 && c.is_ascii_digit())
|
||||
})
|
||||
}) { continue; }
|
||||
(name.to_string(), attr)
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
@@ -964,6 +963,7 @@ impl<'t> Walker<'t> {
|
||||
let mut seen: HashSet<(String, String)> = HashSet::new();
|
||||
for c in cands {
|
||||
if !c.name.starts_with("this.")
|
||||
&& !c.name.contains('.')
|
||||
&& !c.name.contains("::")
|
||||
&& !self.defined_fn_names.contains(&c.name)
|
||||
&& !self.imported_names.contains(&c.name)
|
||||
|
||||
Reference in New Issue
Block a user