From 3de104eea298205820a94e0f234c8f0fda681ff6 Mon Sep 17 00:00:00 2001 From: Aaron Queen Date: Sun, 11 Oct 2026 20:33:17 -0600 Subject: [PATCH] fix(resolve): find the token that starts at a point, not the one ending there The kernel's descendant_for_position took the first child whose span held the point with an inclusive end, so where one token ends exactly where the next begins it returned the earlier one. Callers that pass a token's start got the `;` in `x=1;local function helper()`, which made the Lua local read as global, and the `)` in `if ($x instanceof Foo)$x->bar();`, which hid the PHP guard. tree.ts got the same fix in #308. A child must now end after the point, as in tree.ts. Callers that passed column + 1 to land inside a token now pass the column itself; under the new rule that finds the same node, checked at all 2.37 million token starts of 1,866 real files in 11 languages. Without that change a one-character name or receiver (`a.f()`) would have found the token after it. Golden dumps are unchanged, and json, jq and CPython index to the same edges. --- CHANGELOG.md | 1 + .../kernel-point-lookup-boundary.test.ts | 55 +++++++++++++++++++ codegraph-kernel/src/resolve/call_shape.rs | 2 +- codegraph-kernel/src/resolve/cpp_aliases.rs | 4 +- codegraph-kernel/src/resolve/cpp_range_for.rs | 4 +- codegraph-kernel/src/resolve/cpp_receivers.rs | 10 ++-- codegraph-kernel/src/resolve/cpp_types.rs | 4 +- codegraph-kernel/src/resolve/fields.rs | 4 +- codegraph-kernel/src/resolve/go_asserted.rs | 2 +- codegraph-kernel/src/resolve/iteration.rs | 30 +++++++++- .../src/resolve/kotlin_receiver.rs | 10 ++-- .../src/resolve/kotlin_type_scope.rs | 2 +- .../src/resolve/language_type_scope.rs | 8 +-- codegraph-kernel/src/resolve/lua_alias.rs | 2 +- codegraph-kernel/src/resolve/names.rs | 4 +- .../src/resolve/overloads_upstream.rs | 4 +- codegraph-kernel/src/resolve/receivers.rs | 2 +- .../src/resolve/scala_type_scope.rs | 4 +- 18 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 __tests__/kernel-point-lookup-boundary.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 698a0645e2..9e7ce330bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixes +- **Code written without a space before a name resolves like spaced code.** Where one token ends exactly where the next begins, the resolver read the syntax at that point as the earlier token: in `x=1;local function helper()` it saw the `;`, so the Lua local read as global and calls from other files linked to it, and in `if ($x instanceof Foo)$x->bar();` it saw the `)`, so the PHP guard did not type `$x`. It now reads the token that starts there, the same way the TypeScript side already did. On json, jq and CPython the graph is unchanged. - **An inherited method no longer links to a same-named class in another package.** When a call on a `Child` reached `m()` through `Child extends Base`, the lookup past the first parent matched any class called `Base` in the project, so with an unrelated `Base` in another package the call could land on that package's `Base.m()`, or skip a grandparent's `m()` for it. The lookup now follows the parent the declaration names, for Java, Kotlin, C#, Scala and Swift, where a Java or Kotlin receiver comes from a factory call, or a C#, Scala or Swift receiver is typed. In Scala, a same-named object or a package block in the same file is told apart too; in Swift the other `Base` is one declared in another file, since modules are not modeled. Dart, TypeScript, Python and C++ are unchanged: their wrong links did not reproduce, or Dart's member lookup matches parents by name elsewhere. - **Python calls on a local built through a module, or through one of its fields, now resolve.** `bp = pkg.Blueprint()`, `c.Blueprint()` and `core.Blueprint()` type the local like a class imported by name, and `app.config.from_mapping()` resolves on the class the field holds, as `self.config.from_mapping()` already did. On Flask and requests this adds 98 and 103 call edges (almost all from tests, none removed); a 30-edge sample was all correct. - **A derived TypeScript class no longer treats an unforwarded constructor parameter as a field.** In `class B extends A { constructor(req: Request) { super(); } }`, a call through `this.req` was resolved as if the parameter declared `req`, even when nothing passed it on. A plain constructor parameter now names an inherited field only when the constructor hands it to `super(...)` as an argument; `super(options.req)` does not count. diff --git a/__tests__/kernel-point-lookup-boundary.test.ts b/__tests__/kernel-point-lookup-boundary.test.ts new file mode 100644 index 0000000000..bba56bad32 --- /dev/null +++ b/__tests__/kernel-point-lookup-boundary.test.ts @@ -0,0 +1,55 @@ +/** + * The kernel finds the syntax node at a reference or declaration's start + * position. Where the previous token ends exactly there (`x=1;local function`, + * `instanceof Foo)$x->bar()`), the node at that position is the token that + * starts there, not the `;` or `)` before it. Finding the earlier token made a + * Lua `local` function read as global and hid a PHP `instanceof` guard. + * The spaced forms are the controls: they resolved the same way before. + */ +import { describe, it, expect, afterAll } from 'vitest'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { CodeGraph } from '../src'; + +const roots: string[] = []; +afterAll(() => { + for (const r of roots.splice(0)) fs.rmSync(r, { recursive: true, force: true }); +}); + +async function callTargets(files: Record, from: string): Promise { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-point-lookup-')); + roots.push(root); + for (const [rel, content] of Object.entries(files)) fs.writeFileSync(path.join(root, rel), content); + const cg = await CodeGraph.init(root, { index: true }); + try { + return cg + .getOutgoingEdgesFrom(cg.getNodesInFile(from).map((n) => n.id), ['calls']) + .map((e) => cg.getNode(e.target)!.qualifiedName) + .sort(); + } finally { + cg.close(); + } +} + +describe('point lookup at a token boundary', () => { + it('reads a Lua local function written right after `;` as local', async () => { + const files = { + 'a.lua': 'x=1;local function helper() return 1 end\nlocal y = 2; local function spaced() return 2 end\nreturn x\n', + 'b.lua': 'local function main()\n helper()\n spaced()\nend\nreturn main\n', + }; + expect(await callTargets(files, 'b.lua')).toEqual([]); + }); + + it('applies a PHP instanceof guard to a call written right after `)`', async () => { + const files = { + 'a.php': `bar(); } +function spaced($x) { if ($x instanceof Foo) $x->bar(); } +`, + }; + expect(await callTargets(files, 'a.php')).toEqual(['Foo::bar', 'Foo::bar']); + }); +}); diff --git a/codegraph-kernel/src/resolve/call_shape.rs b/codegraph-kernel/src/resolve/call_shape.rs index aaa03dcb61..cbc7f2b61e 100644 --- a/codegraph-kernel/src/resolve/call_shape.rs +++ b/codegraph-kernel/src/resolve/call_shape.rs @@ -208,7 +208,7 @@ impl KernelResolver { let lines = self.read_file(&r.file_path)?; if r.language=="rust" && r.reference_kind=="calls" { if let Some(tree)=self.parsed_tree(&lines,r) { - let mut node=super::iteration::descendant_for_position(tree.root_node(),lines.text(),((r.line-1).max(0) as usize,r.column.max(0) as usize+1)); + let mut node=super::iteration::descendant_for_position(tree.root_node(),lines.text(),((r.line-1).max(0) as usize,r.column.max(0) as usize)); loop { if node.kind()=="field_expression" { if let (Some(field),Some(value))=(node.child_by_field_name("field"),node.child_by_field_name("value")) { diff --git a/codegraph-kernel/src/resolve/cpp_aliases.rs b/codegraph-kernel/src/resolve/cpp_aliases.rs index 7046a0aa7e..03bd9762e6 100644 --- a/codegraph-kernel/src/resolve/cpp_aliases.rs +++ b/codegraph-kernel/src/resolve/cpp_aliases.rs @@ -152,7 +152,7 @@ impl KernelResolver { let Some(tree) = self.parsed_tree(&source, r) else { continue; }; for keyword in re!(r"\b(?:using|typedef)\b").find_iter(line) { let column = line[..keyword.start()].encode_utf16().count(); - let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), (i, column + 1)); + let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), (i, column)); let mut code = true; loop { if matches!(node.kind(), "comment" | "string_literal" | "raw_string_literal" | "char_literal") { code = false; break; } @@ -312,7 +312,7 @@ impl KernelResolver { let Some(source) = self.read_file(&owner.file_path) else { return Ok(false); }; let site = r.clone().at(owner); let Some(tree) = self.parsed_tree(&source, &site) else { return Ok(false); }; - let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize + 1)); + let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize)); while let Some(parent) = node.parent() { if parent.kind() == "template_declaration" { return Ok(true); } node = parent; diff --git a/codegraph-kernel/src/resolve/cpp_range_for.rs b/codegraph-kernel/src/resolve/cpp_range_for.rs index 1a7dc57676..8d19c5f3f0 100644 --- a/codegraph-kernel/src/resolve/cpp_range_for.rs +++ b/codegraph-kernel/src/resolve/cpp_range_for.rs @@ -174,7 +174,7 @@ impl KernelResolver { if r.language != "cpp" { return Ok(None); } let Some(source) = self.read_file(&r.file_path) else { return Ok(None) }; let Some(tree) = self.parsed_tree(&source, r) else { return Ok(None) }; - let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); while let Some(parent) = at.parent() { // A nearer declaration of the name, `{ Gadget* w = g; w->Self(); }` in the body, hides the loop's. if cpp_binding_type(parent, at, receiver, &source).is_some() { return Ok(None); } @@ -270,7 +270,7 @@ impl KernelResolver { let Some(source) = self.read_file(&function.file_path) else { return Ok(None) }; let site = site_at(r, function); let Some(tree) = self.parsed_tree(&source, &site) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), source.text(), ((function.start_line - 1).max(0) as usize, function.start_column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), source.text(), ((function.start_line - 1).max(0) as usize, function.start_column.max(0) as usize)); let declaration = loop { match node.kind() { "function_definition" | "field_declaration" | "declaration" => break node, diff --git a/codegraph-kernel/src/resolve/cpp_receivers.rs b/codegraph-kernel/src/resolve/cpp_receivers.rs index ced94f9b06..f19303dac3 100644 --- a/codegraph-kernel/src/resolve/cpp_receivers.rs +++ b/codegraph-kernel/src/resolve/cpp_receivers.rs @@ -10,7 +10,7 @@ impl KernelResolver { if !re!(r"^[A-Za-z_]\w*$").is_match(method) { return Ok(None); } let Some(source) = self.read_file(&r.file_path) else { return Ok(None) }; let Some(tree) = self.parsed_tree(&source, r) else { return Ok(None) }; - let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); while at.kind() != "call_expression" { let Some(parent) = at.parent() else { return Ok(None) }; at = parent; } @@ -149,7 +149,7 @@ impl KernelResolver { if r.language != "cpp" || r.reference_kind != "calls" || !re!(r"^[A-Za-z_]\w*$").is_match(&r.reference_name) { return Ok(None); } let Some(source) = self.read_file(&r.file_path) else { return Ok(None) }; let Some(tree) = self.parsed_tree(&source, r) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); loop { if node.kind() == "call_expression" { if let Some(function) = node.child_by_field_name("function").filter(|n| n.kind() == "field_expression") { @@ -240,7 +240,7 @@ impl KernelResolver { let Some(source) = self.read_file(&method.file_path) else { return Ok(None) }; let mut site = r.clone().at(method); site.from_node_id = method.id.clone(); site.column = method.start_column; let Some(tree) = self.parsed_tree(&source, &site) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), source.text(), ((method.start_line - 1).max(0) as usize, method.start_column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), source.text(), ((method.start_line - 1).max(0) as usize, method.start_column.max(0) as usize)); loop { if matches!(node.kind(), "function_definition" | "field_declaration" | "declaration") { return Ok(node.child_by_field_name("type").map(|ty| source.text()[ty.start_byte()..ty.end_byte()].to_string())); @@ -279,7 +279,7 @@ impl KernelResolver { let Some(source) = self.read_file(&owner.file_path) else { return Ok(None) }; let mut site = r.clone().at(owner); site.from_node_id = owner.id.clone(); site.column = owner.start_column; let Some(tree) = self.cpp_field_tree(&source, &site) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize)); while !matches!(node.kind(), "class_specifier" | "struct_specifier" | "union_specifier") { let Some(parent) = node.parent() else { break }; node = parent; } @@ -313,7 +313,7 @@ impl KernelResolver { fn cpp_container_element(&mut self, name: &str, r: &ResolveRefIn) -> Res>> { let Some(source) = self.read_file(&r.file_path) else { return Ok(None) }; if let Some(tree) = self.parsed_tree(&source, r) { - let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut at = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); while let Some(parent) = at.parent() { if let Some(raw) = cpp_binding_type(parent, at, name, &source) { let Some(element) = cpp_element_type(&raw) else { return Ok(None) }; diff --git a/codegraph-kernel/src/resolve/cpp_types.rs b/codegraph-kernel/src/resolve/cpp_types.rs index 04d53a849e..4d7b89c422 100644 --- a/codegraph-kernel/src/resolve/cpp_types.rs +++ b/codegraph-kernel/src/resolve/cpp_types.rs @@ -54,7 +54,7 @@ impl KernelResolver { let mut directives = Vec::new(); if let Some(tree) = self.parsed_tree(&lines, r) { let mut site = super::iteration::descendant_for_position(tree.root_node(), lines.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); let before = site.start_byte(); loop { if matches!(site.kind(), "translation_unit" | "declaration_list" | "field_declaration_list" | "compound_statement") { @@ -174,7 +174,7 @@ impl KernelResolver { let Some(source) = self.read_file(&owner.file_path) else { return Ok(false) }; let mut site = r.clone().at(owner); site.from_node_id = owner.id.clone(); site.column = owner.start_column; let Some(tree) = self.parsed_tree(&source, &site) else { return Ok(false) }; - let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize + 1)); + let mut node = super::iteration::descendant_for_position(tree.root_node(), source.text(), ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize)); while let Some(parent) = node.parent() { if parent.kind() == "template_declaration" { if let Some(parameters) = parent.child_by_field_name("parameters") { diff --git a/codegraph-kernel/src/resolve/fields.rs b/codegraph-kernel/src/resolve/fields.rs index 6e9f3aa67c..b04d26d82a 100644 --- a/codegraph-kernel/src/resolve/fields.rs +++ b/codegraph-kernel/src/resolve/fields.rs @@ -178,7 +178,7 @@ impl KernelResolver { let Some(tree) = self.parsed_tree(&source, &site) else { return Ok(false); }; let mut declaration = super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((target.start_line - 1).max(0) as usize, target.start_column.max(0) as usize + 1), + ((target.start_line - 1).max(0) as usize, target.start_column.max(0) as usize), ); while !matches!(declaration.kind(), "type_spec" | "type_alias") { let Some(parent) = declaration.parent() else { return Ok(false); }; @@ -231,7 +231,7 @@ impl KernelResolver { if let Some(tree) = self.parsed_tree(&lines, &site) { let mut declaration = super::iteration::descendant_for_position( tree.root_node(), lines.text(), - ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize + 1), + ((owner.start_line - 1).max(0) as usize, owner.start_column.max(0) as usize), ); while !matches!(declaration.kind(), "type_alias" | "type_spec") { let Some(parent) = declaration.parent() else { break; }; diff --git a/codegraph-kernel/src/resolve/go_asserted.rs b/codegraph-kernel/src/resolve/go_asserted.rs index fe681c04e5..5bb1e01aa2 100644 --- a/codegraph-kernel/src/resolve/go_asserted.rs +++ b/codegraph-kernel/src/resolve/go_asserted.rs @@ -151,7 +151,7 @@ impl KernelResolver { if !text.contains(".(") { return None; } let tree = self.parsed_tree(&file, r)?; let site = super::iteration::descendant_for_position(tree.root_node(), text, - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); let at = site.start_byte(); let mut scope = Some(site); while let Some(node) = scope { diff --git a/codegraph-kernel/src/resolve/iteration.rs b/codegraph-kernel/src/resolve/iteration.rs index 3e6336b04e..c2ccd91b07 100644 --- a/codegraph-kernel/src/resolve/iteration.rs +++ b/codegraph-kernel/src/resolve/iteration.rs @@ -27,7 +27,10 @@ fn before(a: (usize, usize), b: (usize, usize)) -> bool { } /// descendantForPosition (tree.ts): descend into the first child, named or -/// not, whose span contains the point, until none does. +/// not, whose span contains the point, until none does. A child must end +/// after the point, so where one token ends and the next begins (`)` then +/// `Foo` in `if(ok)Foo f;`) the next one is found: pass a token's start to +/// find that token. pub(super) fn descendant_for_position<'t>(root: TsNode<'t>, text: &str, at: (usize, usize)) -> TsNode<'t> { let mut node = root; 'down: loop { @@ -35,7 +38,7 @@ pub(super) fn descendant_for_position<'t>(root: TsNode<'t>, text: &str, at: (usi for c in node.children(&mut cursor) { let start = point16(text, c.start_byte(), c.start_position()); let end = point16(text, c.end_byte(), c.end_position()); - if before(start, at) && before(at, end) { + if before(start, at) && !before(end, at) { node = c; continue 'down; } @@ -266,7 +269,7 @@ impl KernelResolver { pub(super) fn kotlin_chain_receiver_call(&mut self, r: &ResolveRefIn) -> Option { let file = self.read_file(&r.file_path)?; let tree = self.parsed_tree(&file, r)?; - let mut node = descendant_for_position(tree.root_node(), file.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), file.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); if node.kind() != "simple_identifier" { return None; } while let Some(parent) = node.parent() { if parent.kind() == "navigation_expression" { @@ -753,6 +756,27 @@ impl KernelResolver { mod tests { use super::kotlin_takes_trailing_lambda as trailing; + /// The node `descendant_for_position` finds at (0, `column`) of `code`. + fn token_at(code: &str, language: &str, column: usize) -> (String, String) { + let mut parser = tree_sitter::Parser::new(); + parser.set_language(&crate::langs::grammar_for(language).unwrap()).unwrap(); + let tree = parser.parse(code, None).unwrap(); + let node = super::descendant_for_position(tree.root_node(), code, (0, column)); + (node.kind().to_string(), code[node.start_byte()..node.end_byte()].to_string()) + } + + #[test] + fn a_point_where_one_token_ends_and_the_next_begins_finds_the_next() { + // `)` ends where the name starts; the name is the token at that point. + let code = "void g(){if(ok)Foo f;}"; + assert_eq!(token_at(code, "cpp", code.find("Foo").unwrap()).1, "Foo"); + let code = "void g(){if(ok)target();}"; + assert_eq!(token_at(code, "cpp", code.find("target").unwrap()), ("identifier".into(), "target".into())); + // A one-character name followed by `(`: its start finds the name. + let code = "void g(){f(x);}"; + assert_eq!(token_at(code, "cpp", code.find("f(").unwrap()), ("identifier".into(), "f".into())); + } + #[test] fn a_trailing_lambda_needs_a_function_typed_last_parameter() { for sig in [ diff --git a/codegraph-kernel/src/resolve/kotlin_receiver.rs b/codegraph-kernel/src/resolve/kotlin_receiver.rs index b7ee487b91..99596c5253 100644 --- a/codegraph-kernel/src/resolve/kotlin_receiver.rs +++ b/codegraph-kernel/src/resolve/kotlin_receiver.rs @@ -23,7 +23,7 @@ impl KernelResolver { if r.language != "kotlin" || r.reference_kind != "calls" { return Ok(None); } let Some(file) = self.read_file(&r.file_path) else { return Ok(None) }; let Some(tree) = self.parsed_tree(&file, r) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), file.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), file.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); if node.kind() != "simple_identifier" { return Ok(None); } while let Some(parent) = node.parent() { if parent.kind() == "navigation_expression" { @@ -99,7 +99,7 @@ impl KernelResolver { fn kotlin_property_chain_receiver(&mut self,r:&ResolveRefIn)->bool { let Some(file)=self.read_file(&r.file_path) else {return false}; let Some(tree)=self.parsed_tree(&file,r) else {return false}; - let mut node=descendant_for_position(tree.root_node(),file.text(),((r.line-1).max(0) as usize,r.column.max(0) as usize+1)); + let mut node=descendant_for_position(tree.root_node(),file.text(),((r.line-1).max(0) as usize,r.column.max(0) as usize)); while let Some(parent)=node.parent() { if parent.kind()=="navigation_expression" { let Some(receiver)=parent.named_child(0) else {return false}; @@ -144,7 +144,7 @@ impl KernelResolver { } fn kotlin_decl_tree_node<'t>(tree: &'t tree_sitter::Tree, source: &str, decl: &KNode, kind: &str) -> Option> { - let mut node=descendant_for_position(tree.root_node(),source,((decl.start_line-1).max(0) as usize,decl.start_column.max(0) as usize+1)); + let mut node=descendant_for_position(tree.root_node(),source,((decl.start_line-1).max(0) as usize,decl.start_column.max(0) as usize)); while node.kind()!=kind {node=node.parent()?;} Some(node) } @@ -468,7 +468,7 @@ impl KernelResolver { let Some(file) = self.read_file(&decl.file_path) else { return Ok(None) }; let site = r.clone().at(decl); let Some(tree) = self.parsed_tree(&file, &site) else { return Ok(None) }; - let mut node = descendant_for_position(tree.root_node(), file.text(), ((decl.start_line - 1).max(0) as usize, decl.start_column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), file.text(), ((decl.start_line - 1).max(0) as usize, decl.start_column.max(0) as usize)); while node.kind() != "property_declaration" { let Some(parent) = node.parent() else { return Ok(None) }; node = parent; } @@ -481,7 +481,7 @@ impl KernelResolver { let Some(file) = self.read_file(&decl.file_path) else { return Ok(false) }; let site = r.clone().at(decl); let Some(tree) = self.parsed_tree(&file, &site) else { return Ok(false) }; - let mut node = descendant_for_position(tree.root_node(), file.text(), ((decl.start_line - 1).max(0) as usize, decl.start_column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), file.text(), ((decl.start_line - 1).max(0) as usize, decl.start_column.max(0) as usize)); while node.kind() != "function_declaration" { let Some(parent) = node.parent() else { return Ok(false) }; node = parent; } diff --git a/codegraph-kernel/src/resolve/kotlin_type_scope.rs b/codegraph-kernel/src/resolve/kotlin_type_scope.rs index 16f857fa8e..7e5e6ce8aa 100644 --- a/codegraph-kernel/src/resolve/kotlin_type_scope.rs +++ b/codegraph-kernel/src/resolve/kotlin_type_scope.rs @@ -77,7 +77,7 @@ impl KernelResolver { source.text(), ( (declaration.start_line - 1).max(0) as usize, - declaration.start_column.max(0) as usize + 1, + declaration.start_column.max(0) as usize, ), ); while node.kind() != "function_declaration" { diff --git a/codegraph-kernel/src/resolve/language_type_scope.rs b/codegraph-kernel/src/resolve/language_type_scope.rs index f7d3232959..004cc0ab68 100644 --- a/codegraph-kernel/src/resolve/language_type_scope.rs +++ b/codegraph-kernel/src/resolve/language_type_scope.rs @@ -272,7 +272,7 @@ impl KernelResolver { source.text(), ( (n.start_line - 1).max(0) as usize, - n.start_column.max(0) as usize + 1, + n.start_column.max(0) as usize, ), ); loop { @@ -306,7 +306,7 @@ impl KernelResolver { let mut node = super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1), + ((r.line - 1).max(0) as usize, r.column.max(0) as usize), ); let mut scopes = Vec::new(); loop { @@ -399,7 +399,7 @@ impl KernelResolver { let mut node = super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1), + ((r.line - 1).max(0) as usize, r.column.max(0) as usize), ); let mut names = Vec::new(); loop { @@ -726,7 +726,7 @@ impl KernelResolver { source.text(), ( (n.start_line - 1).max(0) as usize, - n.start_column.max(0) as usize + 1, + n.start_column.max(0) as usize, ), ); while let Some(parent) = node.parent() { diff --git a/codegraph-kernel/src/resolve/lua_alias.rs b/codegraph-kernel/src/resolve/lua_alias.rs index 758d90df0f..fddf67729c 100644 --- a/codegraph-kernel/src/resolve/lua_alias.rs +++ b/codegraph-kernel/src/resolve/lua_alias.rs @@ -24,7 +24,7 @@ impl KernelResolver { let decl = self.lua_local_decl(&r.reference_name, r)?; if let Some(source) = self.read_file(&r.file_path) { if let Some(tree) = self.parsed_tree(&source, r) { - let mut node = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + let mut node = descendant_for_position(tree.root_node(), source.text(), ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); while let Some(parent) = node.parent() { if parent.kind() == "for_statement" && node.kind() == "block" { let clause = named_children(parent).into_iter().find(|n| matches!(n.kind(), "for_generic_clause" | "for_numeric_clause")); diff --git a/codegraph-kernel/src/resolve/names.rs b/codegraph-kernel/src/resolve/names.rs index b0acf4fafd..5342c123bd 100644 --- a/codegraph-kernel/src/resolve/names.rs +++ b/codegraph-kernel/src/resolve/names.rs @@ -1109,7 +1109,7 @@ impl KernelResolver { let Some(file) = self.read_file(&r.file_path) else { return Ok(None); }; let Some(tree) = self.parsed_tree(&file, r) else { return Ok(None); }; let mut node = super::iteration::descendant_for_position(tree.root_node(), file.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); let mut call = None; loop { if node.kind() == "call_expression" { @@ -1170,7 +1170,7 @@ impl KernelResolver { let file = self.read_file(&r.file_path)?; let tree = self.parsed_tree(&file, r)?; let mut node = super::iteration::descendant_for_position(tree.root_node(), file.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1)); + ((r.line - 1).max(0) as usize, r.column.max(0) as usize)); loop { if node.kind() == "call_expression" { if let Some(function) = node.child_by_field_name("function") { diff --git a/codegraph-kernel/src/resolve/overloads_upstream.rs b/codegraph-kernel/src/resolve/overloads_upstream.rs index 1b91795f39..aa109ab558 100644 --- a/codegraph-kernel/src/resolve/overloads_upstream.rs +++ b/codegraph-kernel/src/resolve/overloads_upstream.rs @@ -390,7 +390,7 @@ impl KernelResolver { declaration_source.text(), ( (method.start_line - 1).max(0) as usize, - method.start_column.max(0) as usize + 1, + method.start_column.max(0) as usize, ), ); loop { @@ -611,7 +611,7 @@ impl KernelResolver { let mut call = super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1), + ((r.line - 1).max(0) as usize, r.column.max(0) as usize), ); let callee_pattern = Self::cached_regex(&format!( r"(?:^|[.:>])\s*{}(?:\s*<.*>)?\s*$", diff --git a/codegraph-kernel/src/resolve/receivers.rs b/codegraph-kernel/src/resolve/receivers.rs index 9067883f6e..60726cecf5 100644 --- a/codegraph-kernel/src/resolve/receivers.rs +++ b/codegraph-kernel/src/resolve/receivers.rs @@ -267,7 +267,7 @@ impl KernelResolver { let text = file.text(); let row = (site.line - 1).max(0) as usize; let column = site.column.max(0) as usize; - let call = descendant_for_position(tree.root_node(), text, (row, column + 1)); + let call = descendant_for_position(tree.root_node(), text, (row, column)); let contains_receiver = |pattern: tree_sitter::Node<'_>| { let mut pending = vec![pattern]; while let Some(name) = pending.pop() { diff --git a/codegraph-kernel/src/resolve/scala_type_scope.rs b/codegraph-kernel/src/resolve/scala_type_scope.rs index b1ecf78edc..2d25f770c0 100644 --- a/codegraph-kernel/src/resolve/scala_type_scope.rs +++ b/codegraph-kernel/src/resolve/scala_type_scope.rs @@ -158,7 +158,7 @@ impl KernelResolver { let mut at = super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1), + ((r.line - 1).max(0) as usize, r.column.max(0) as usize), ); let mut scopes = Vec::new(); loop { @@ -200,7 +200,7 @@ impl KernelResolver { super::iteration::descendant_for_position( tree.root_node(), source.text(), - ((r.line - 1).max(0) as usize, r.column.max(0) as usize + 1), + ((r.line - 1).max(0) as usize, r.column.max(0) as usize), ) .end_byte(), );