Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion codegraph-kernel/src/resolve/js_scope_upstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ fn visible(scope: Option<(Point, Point)>, point: Point) -> bool {
scope.is_none_or(|(start, end)| point >= start && point <= end)
}

/// jsDeclarationReachable's memoized walk result per (candidate, ref
/// language). Some(Some(s)): declarator found, scope s. Some(None):
/// found, global scope (always visible). None: no declarator — verdict
/// None.
pub(super) type JsDeclScope = Option<Option<(Point, Point)>>;

impl KernelResolver {
/// Parse embedded scripts with the original file's byte and line positions.
pub(super) fn js_binding_tree(&mut self, source: &Rc<SourceFile>, r: &ResolveRefIn) -> Option<Rc<tree_sitter::Tree>> {
Expand Down Expand Up @@ -315,6 +321,17 @@ impl KernelResolver {
(n.start_line - 1).max(0) as usize,
super::names::js_unit_to_byte(line, n.start_column.max(0) as usize),
);
// The declarator walk below depends only on the candidate — the ref
// enters only through `visible(scope, point)`. Memoize the walk so a
// name with hundreds of same-file candidates pays the parent-chain
// climb once, not once per ref (minified bundles are the hot case).
// Keyed by ref language too: parsed_tree picks its parser from
// r.language, so the same candidate walks a different tree under
// typescript vs javascript refs (review on #566).
let memo_key = (n.id.clone(), r.language.clone());
if let Some(&hit) = self.js_decl_scope_memo.get(&memo_key) {
return hit.map(|scope| visible(scope, point));
}
let mut current = tree
.root_node()
.descendant_for_point_range(declaration, declaration);
Expand All @@ -328,10 +345,13 @@ impl KernelResolver {
.child_by_field_name("name")
.is_some_and(|name| source.text()[name.start_byte()..name.end_byte()] == n.name)
{
return Some(visible(binding_scope(node), point));
let scope = binding_scope(node);
self.js_decl_scope_memo.insert(memo_key.clone(), Some(scope));
return Some(visible(scope, point));
}
current = node.parent();
}
self.js_decl_scope_memo.insert(memo_key, None);
None
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down
5 changes: 5 additions & 0 deletions codegraph-kernel/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,10 @@ pub struct KernelResolver {
manifest_external_memo: HashMap<String, Rc<HashSet<String>>>,
/// lexicalScopeOf, by candidate id: the scoping function body's lines.
lexical_scope_memo: HashMap<String, Option<(i64, i64)>>,
/// jsDeclarationReachable's candidate-side walk, by (candidate id,
/// ref language) — parsed_tree selects its parser from the ref's
/// language, so the same candidate can walk a different tree.
js_decl_scope_memo: HashMap<(String, String), js_scope_upstream::JsDeclScope>,
language_type_scope_memo: HashMap<String, Rc<language_type_scope::LanguageTypeScope>>,
scala_package_object_membership_memo: HashMap<String, Option<(String, i64, i64)>>,
cpp_default_declaration_memo: HashMap<String, declarations_upstream::CppDefaultDeclarationIndex>,
Expand Down Expand Up @@ -978,6 +982,7 @@ impl KernelResolver {
manifest_own_memo: HashMap::new(),
manifest_external_memo: HashMap::new(),
lexical_scope_memo: HashMap::new(),
js_decl_scope_memo: HashMap::new(),
language_type_scope_memo: HashMap::new(),
scala_package_object_membership_memo: HashMap::new(),
cpp_default_declaration_memo: HashMap::new(),
Expand Down
Loading