mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-12 19:55:14 +02:00
[pitboss/grind] deferred session-0002 (20260516T052512Z-20f8)
This commit is contained in:
parent
7a2f82c2ab
commit
282acddbbf
11 changed files with 214 additions and 45 deletions
|
|
@ -3958,3 +3958,42 @@ fn rhs_array_literal_elements_recognise_per_language_shapes() {
|
|||
// Non-array-shape node returns empty (defensive guard).
|
||||
assert!(run("javascript", b"const x = tainted;\n", &["identifier"]).is_empty());
|
||||
}
|
||||
|
||||
/// `CalleeSite.span` should carry the 1-based (line, col) of each call's
|
||||
/// node span so downstream consumers (surface map, datastore/external
|
||||
/// detectors) can render real coordinates instead of `line: 0`.
|
||||
#[test]
|
||||
fn callee_site_span_carries_line_and_column() {
|
||||
// Three calls on three different lines. The leading newline puts
|
||||
// line 1 at the blank line; `helper(x, y);` is on line 3, etc.
|
||||
let src = b"
|
||||
function outer(obj, x, y) {
|
||||
helper(x, y);
|
||||
obj.method(x);
|
||||
}
|
||||
";
|
||||
let ts_lang = Language::from(tree_sitter_javascript::LANGUAGE);
|
||||
let file_cfg = parse_to_file_cfg(src, "javascript", ts_lang);
|
||||
let (_key, outer) = file_cfg
|
||||
.summaries
|
||||
.iter()
|
||||
.find(|(k, _)| k.name == "outer")
|
||||
.expect("outer summary should exist");
|
||||
|
||||
let helper_site = outer
|
||||
.callees
|
||||
.iter()
|
||||
.find(|c| c.name == "helper")
|
||||
.expect("helper call should be recorded");
|
||||
let (line, col) = helper_site.span.expect("span populated at CFG-build time");
|
||||
assert_eq!(line, 3, "helper(...) sits on the 3rd source line");
|
||||
assert!(col >= 5, "indented 4 spaces — column is 1-based and > 4");
|
||||
|
||||
let method_site = outer
|
||||
.callees
|
||||
.iter()
|
||||
.find(|c| c.name.ends_with("method"))
|
||||
.expect("method call should be recorded");
|
||||
let (mline, _) = method_site.span.expect("method span populated");
|
||||
assert_eq!(mline, 4, "obj.method(x) on line 4");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5664,7 +5664,7 @@ pub(super) fn build_sub<'a>(
|
|||
for idx in fn_graph.node_indices() {
|
||||
let info = &fn_graph[idx];
|
||||
if let Some(callee) = &info.call.callee {
|
||||
let site = build_callee_site(callee, info, lang);
|
||||
let site = build_callee_site(callee, info, lang, code);
|
||||
// Dedup by (name, arity, receiver, qualifier, ordinal). A
|
||||
// single function may legitimately contain multiple distinct
|
||||
// calls to the same callee (e.g. different ordinals or
|
||||
|
|
@ -6632,7 +6632,12 @@ fn apply_gated_label_rules(
|
|||
/// remains the single segment immediately before the leaf (back-compat
|
||||
/// with the legacy heuristic). For method calls the qualifier is
|
||||
/// redundant with `receiver` and is left `None`.
|
||||
fn build_callee_site(callee: &str, info: &NodeInfo, lang: &str) -> crate::summary::CalleeSite {
|
||||
fn build_callee_site(
|
||||
callee: &str,
|
||||
info: &NodeInfo,
|
||||
lang: &str,
|
||||
code: &[u8],
|
||||
) -> crate::summary::CalleeSite {
|
||||
use crate::summary::CalleeSite;
|
||||
|
||||
let receiver = info.call.receiver.clone();
|
||||
|
|
@ -6661,15 +6666,39 @@ fn build_callee_site(callee: &str, info: &NodeInfo, lang: &str) -> crate::summar
|
|||
None
|
||||
};
|
||||
|
||||
let span = callee_span_line_col(code, info.ast.span.0);
|
||||
|
||||
CalleeSite {
|
||||
name: callee.to_string(),
|
||||
arity,
|
||||
receiver,
|
||||
qualifier,
|
||||
ordinal: info.call.call_ordinal,
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a byte offset into a 1-based `(line, col)` pair against `code`.
|
||||
///
|
||||
/// Returns `None` only when `code` is empty (no source to resolve against);
|
||||
/// out-of-range offsets are clamped to `code.len()` so a synthetic node
|
||||
/// whose span overshoots the file still produces the last-line coordinate
|
||||
/// rather than `None`.
|
||||
fn callee_span_line_col(code: &[u8], offset: usize) -> Option<(u32, u32)> {
|
||||
if code.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let clamped = offset.min(code.len());
|
||||
let prefix = &code[..clamped];
|
||||
let line = prefix.iter().filter(|&&b| b == b'\n').count() as u32 + 1;
|
||||
let col_bytes = match prefix.iter().rposition(|&b| b == b'\n') {
|
||||
Some(idx) => clamped - idx - 1,
|
||||
None => clamped,
|
||||
} as u32
|
||||
+ 1;
|
||||
Some((line, col_bytes))
|
||||
}
|
||||
|
||||
/// Convert the graph‑local `FuncSummaries` into serialisable [`FuncSummary`]
|
||||
/// values suitable for cross‑file persistence.
|
||||
pub(crate) fn export_summaries(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue