mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-22 01:07:18 +08:00
Merge remote-tracking branch 'origin/main' into fix/kotlin-property-initializer-walk
# Conflicts: # CHANGELOG.md # __tests__/fixtures/kernel-parity/torture.js # __tests__/fixtures/kernel-parity/torture.py
This commit is contained in:
@@ -52,6 +52,10 @@
|
||||
//! per-caller targets (insertion-ordered, branch reassignments accumulate);
|
||||
//! a later bare `k(args)` emits one `calls` ref PER target and suppresses
|
||||
//! the local name. Template args stripped like base-class refs (#1043).
|
||||
//! - pure-virtual methods (#1727): cpp in-class `virtual T f(...) = 0;` is a
|
||||
//! `field_declaration` (not `function_definition`); mint a method node so
|
||||
//! abstract-base calls and cpp-override synthesis have a target. Mirrors
|
||||
//! TS `methodTypes` + `classifyMethodNode` / `isAbstract`.
|
||||
//! - stack construction (#1035): cpp `declaration` with class-like named
|
||||
//! `type` and an init_declarator whose value is argument_list /
|
||||
//! initializer_list → `instantiates` (most-vexing-parse excluded).
|
||||
@@ -66,7 +70,7 @@
|
||||
|
||||
use crate::buffers::{
|
||||
build_meta, edge_kind_index, node_kind_index, Arena, BoolFlags, EdgeRow, EmitOut, NodeRow,
|
||||
RefRow, StrRef, Tables, FLAG_IS_EXPORTED, FUNCTION_REF_CODE, NONE, NONE_STR,
|
||||
RefRow, StrRef, Tables, FLAG_IS_ABSTRACT, FLAG_IS_EXPORTED, FUNCTION_REF_CODE, NONE, NONE_STR,
|
||||
};
|
||||
use crate::docstring::preceding_docstring;
|
||||
use crate::ids;
|
||||
@@ -289,6 +293,7 @@ struct Extra {
|
||||
signature: Option<String>,
|
||||
visibility: Option<u8>,
|
||||
is_exported: Option<bool>,
|
||||
is_abstract: Option<bool>,
|
||||
return_type: Option<String>,
|
||||
qualified_name: Option<String>,
|
||||
}
|
||||
@@ -508,6 +513,9 @@ impl<'t> Walker<'t> {
|
||||
if let Some(v) = extra.is_exported {
|
||||
flags.set(FLAG_IS_EXPORTED, v);
|
||||
}
|
||||
if let Some(v) = extra.is_abstract {
|
||||
flags.set(FLAG_IS_ABSTRACT, v);
|
||||
}
|
||||
let name_ref = self.arena.put(name);
|
||||
let qn_ref = self.arena.put(&qualified);
|
||||
let id_ref = self.arena.put(&id);
|
||||
@@ -740,6 +748,36 @@ impl<'t> Walker<'t> {
|
||||
.any(|c| c.kind() == "type_qualifier" && self.text(c) == "const")
|
||||
}
|
||||
|
||||
/// `#1727`: C++ pure-virtual method declaration (`virtual int read(int key) = 0;`).
|
||||
/// tree-sitter-cpp shapes these as `field_declaration` whose declarator unwraps
|
||||
/// to a `function_declarator`, with the pure-virtual `= 0` as a DIRECT
|
||||
/// `number_literal` "0" child (default-arg `= 0` lives inside
|
||||
/// `parameter_declaration` and must not match).
|
||||
fn is_cpp_pure_virtual_method_decl(&self, node: Node<'_>) -> bool {
|
||||
if node.kind() != "field_declaration" {
|
||||
return false;
|
||||
}
|
||||
let Some(mut declarator) = node.child_by_field_name("declarator") else {
|
||||
return false;
|
||||
};
|
||||
while matches!(declarator.kind(), "pointer_declarator" | "reference_declarator") {
|
||||
let inner = declarator
|
||||
.child_by_field_name("declarator")
|
||||
.or_else(|| declarator.named_child(0));
|
||||
let Some(inner) = inner else {
|
||||
return false;
|
||||
};
|
||||
declarator = inner;
|
||||
}
|
||||
if declarator.kind() != "function_declarator" {
|
||||
return false;
|
||||
}
|
||||
(0..node.named_child_count())
|
||||
.filter_map(|i| node.named_child(i))
|
||||
.any(|c| c.kind() == "number_literal" && self.text(c) == "0")
|
||||
}
|
||||
|
||||
|
||||
/// cppExtractor.isMisparsedFunction (languages/c-cpp.ts:811). cpp only.
|
||||
fn is_misparsed_function(&self, name: &str, node: Node) -> bool {
|
||||
if self.variant != Variant::Cpp {
|
||||
@@ -830,6 +868,17 @@ impl<'t> Walker<'t> {
|
||||
self.extract_variable(node);
|
||||
self.scan_fn_ref_subtree(node, 0);
|
||||
skip_children = true;
|
||||
} else if self.variant == Variant::Cpp
|
||||
&& kind == "field_declaration"
|
||||
&& self.inside_class_like()
|
||||
&& self.is_cpp_pure_virtual_method_decl(node)
|
||||
{
|
||||
// Pure-virtual methods have no `function_definition` body — mint the
|
||||
// method node so calls through the abstract base and cpp-override
|
||||
// synthesis have a target (#1727). Non-pure field_declarations fall
|
||||
// through to the children walk (data members / prototypes).
|
||||
self.extract_method(node);
|
||||
skip_children = true;
|
||||
} else if kind == "preproc_include" {
|
||||
self.extract_import(node);
|
||||
} else if kind == "call_expression" {
|
||||
@@ -914,6 +963,11 @@ impl<'t> Walker<'t> {
|
||||
let extra = Extra {
|
||||
docstring: preceding_docstring(node, self.src),
|
||||
visibility: if self.variant == Variant::Cpp { self.visibility_of(node) } else { None },
|
||||
is_abstract: if self.variant == Variant::Cpp && self.is_cpp_pure_virtual_method_decl(node) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
return_type: self.return_type_of(node),
|
||||
qualified_name: receiver_type
|
||||
.as_ref()
|
||||
|
||||
@@ -631,6 +631,13 @@ impl<'t> Walker<'t> {
|
||||
} else {
|
||||
callee_name = method_name.to_string();
|
||||
}
|
||||
} else if let Some(r) = receiver.filter(|r| r.kind() == "call") {
|
||||
// Call receiver — `d.setdefault(k, []).append(v)` (#1683):
|
||||
// `<inner>().<method>`, or nothing when the inner callee
|
||||
// is not a plain name / attribute chain. Mirrors
|
||||
// TreeSitterExtractor.extractCall.
|
||||
let Some(inner) = self.plain_inner_callee(r) else { return };
|
||||
callee_name = format!("{inner}().{method_name}");
|
||||
} else {
|
||||
callee_name = method_name.to_string();
|
||||
}
|
||||
@@ -649,6 +656,22 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The callee of a call receiver when it is a plain identifier or attribute
|
||||
/// chain (`make`, `d.setdefault`), whitespace stripped (#1683).
|
||||
fn plain_inner_callee(&self, call: Node<'t>) -> Option<String> {
|
||||
let inner = call.child_by_field_name("function")?;
|
||||
let text: String = self.text(inner).chars().filter(|c| !c.is_whitespace()).collect();
|
||||
if text.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let ok = text.split('.').all(|seg| {
|
||||
let mut chars = seg.chars();
|
||||
matches!(chars.next(), Some(c) if c.is_ascii_alphabetic() || c == '_')
|
||||
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||
});
|
||||
if ok { Some(text) } else { None }
|
||||
}
|
||||
|
||||
/// extractDecoratorsFor — python decorators are PRECEDING SIBLINGS inside
|
||||
/// decorated_definition. Only bare-identifier decorators yield a target
|
||||
/// (python's `call` kind isn't `call_expression`, and `attribute` isn't in
|
||||
|
||||
@@ -23,7 +23,7 @@ impl<'t> Walker<'t> {
|
||||
// variable_declarator (`export const useAuth = () => {}`).
|
||||
if name_override.is_none()
|
||||
&& name == "<anonymous>"
|
||||
&& matches!(node.kind(), "arrow_function" | "function_expression")
|
||||
&& matches!(node.kind(), "arrow_function" | "function_expression" | "generator_function")
|
||||
{
|
||||
if let Some(parent) = node.parent() {
|
||||
if parent.kind() == "variable_declarator" {
|
||||
@@ -342,9 +342,9 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
let name = self.text(name_node).to_string();
|
||||
|
||||
// Arrow/function values extract as functions, named by the declarator.
|
||||
// Arrow/function/generator values extract as functions, named by the declarator.
|
||||
if let Some(v) = value {
|
||||
if matches!(v.kind(), "arrow_function" | "function_expression") {
|
||||
if matches!(v.kind(), "arrow_function" | "function_expression" | "generator_function") {
|
||||
self.extract_function(v, None);
|
||||
continue;
|
||||
}
|
||||
@@ -1111,9 +1111,14 @@ impl<'t> Walker<'t> {
|
||||
} else {
|
||||
callee_name = method_name.to_string();
|
||||
}
|
||||
} else if let Some(r) = receiver.filter(|r| r.kind() == "call_expression") {
|
||||
// Call receiver — `make().run()` (#1683): keep the inner
|
||||
// callee as `<inner>().<method>`, or emit nothing when it
|
||||
// is not a plain name / member chain. Mirrors
|
||||
// TreeSitterExtractor.extractCall.
|
||||
let Some(inner) = self.plain_inner_callee(r) else { return };
|
||||
callee_name = format!("{inner}().{method_name}");
|
||||
} else {
|
||||
// (the call-receiver re-encode branches are other
|
||||
// languages'; TS/JS keeps the bare method name)
|
||||
callee_name = method_name.to_string();
|
||||
}
|
||||
}
|
||||
@@ -1136,6 +1141,22 @@ impl<'t> Walker<'t> {
|
||||
|
||||
// --- extractInstantiation -----------------------------------------------------------
|
||||
|
||||
/// The callee of a call-expression receiver when it is a plain identifier
|
||||
/// or member chain (`make`, `d.setdefault`), whitespace stripped (#1683).
|
||||
fn plain_inner_callee(&self, call: Node<'t>) -> Option<String> {
|
||||
let inner = call.child_by_field_name("function")?;
|
||||
let text: String = self.text(inner).chars().filter(|c| !c.is_whitespace()).collect();
|
||||
if text.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let ok = text.split('.').all(|seg| {
|
||||
let mut chars = seg.chars();
|
||||
matches!(chars.next(), Some(c) if c.is_ascii_alphabetic() || c == '_' || c == '$')
|
||||
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
|
||||
});
|
||||
if ok { Some(text) } else { None }
|
||||
}
|
||||
|
||||
pub(super) fn extract_instantiation(&mut self, node: Node<'t>) {
|
||||
if self.stack.is_empty() {
|
||||
return;
|
||||
|
||||
@@ -60,7 +60,7 @@ fn is_method_type(v: Variant, kind: &str) -> bool {
|
||||
}
|
||||
|
||||
fn is_function_type(kind: &str) -> bool {
|
||||
matches!(kind, "function_declaration" | "arrow_function" | "function_expression")
|
||||
matches!(kind, "function_declaration" | "generator_function_declaration" | "arrow_function" | "function_expression" | "generator_function")
|
||||
}
|
||||
|
||||
fn is_class_type(v: Variant, kind: &str) -> bool {
|
||||
@@ -792,7 +792,7 @@ impl<'t> Walker<'t> {
|
||||
if let Some(name_node) = node.child_by_field_name("name") {
|
||||
return self.text(name_node).to_string();
|
||||
}
|
||||
if matches!(node.kind(), "arrow_function" | "function_expression") {
|
||||
if matches!(node.kind(), "arrow_function" | "function_expression" | "generator_function") {
|
||||
return "<anonymous>".to_string();
|
||||
}
|
||||
for i in 0..node.named_child_count() {
|
||||
|
||||
Reference in New Issue
Block a user