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
210 changes: 1 addition & 209 deletions crates/codegraph-resolve/src/frameworks/tauri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use std::sync::OnceLock;
use codegraph_core::types::{EdgeKind, Language, Node, NodeKind};

use crate::framework::FrameworkResolver;
use crate::rust_lexical::{read_ident, rust_code_mask};
use crate::types::{RefView, ResolutionContext, ResolvedBy, ResolvedRef};

/// The Rust attribute that marks a function as an IPC command. Held as a `&str`
Expand Down Expand Up @@ -451,25 +452,6 @@ fn skip_balanced(bytes: &[u8], mask: &[bool], i: usize, open: u8, close: u8) ->
None
}

fn read_ident(bytes: &[u8], i: usize) -> Option<(&str, usize)> {
if i >= bytes.len() || !is_ident_start(bytes[i]) {
return None;
}
let mut j = i;
while j < bytes.len() && is_ident_byte(bytes[j]) {
j += 1;
}
std::str::from_utf8(&bytes[i..j]).ok().map(|s| (s, j))
}

fn is_ident_start(b: u8) -> bool {
b.is_ascii_alphabetic() || b == b'_' || b >= 0x80
}

fn is_ident_byte(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'_' || b >= 0x80
}

fn is_rust_source(path: &str) -> bool {
path.ends_with(".rs") && !FIXTURE_PREFIXES.iter().any(|p| path.starts_with(p))
}
Expand All @@ -480,196 +462,6 @@ fn is_js_family(path: &str) -> bool {
.any(|ext| path.ends_with(ext))
}

// ---------------------------------------------------------------------------
// The Rust lexical mask
// ---------------------------------------------------------------------------

/// Byte mask over `src`: `true` where the byte is ordinary Rust code.
///
/// One forward scan tracking the states a textual attribute match can hide in,
/// each of which was measured to fabricate an edge (or, for the last one, to lose
/// a real command) when unhandled:
///
/// * line comments, including `///` and `//!`;
/// * block comments, **nestable** — Rust nests, unlike C++, so a C++-shaped mask
/// closes at the first `*/` and reads the trailing bytes as code;
/// * raw strings `r"…"` / `r#"…"#` and their byte forms `br##"…"##`, with the
/// HASH COUNT captured so a custom delimiter still closes correctly;
/// * normal and byte strings, `"…"` / `b"…"`, with `\` escapes;
/// * char literals with `\` escapes (`'\''`) that DECLINE to open on a lifetime
/// `'a` — a mask that treats every `'` as a delimiter masks from the lifetime
/// forward and can swallow a genuine attribute, which is a silent feature death
/// rather than a fabrication.
fn rust_code_mask(src: &str) -> Vec<bool> {
let bytes = src.as_bytes();
let mut mask = vec![true; bytes.len()];
let mut i = 0usize;
while i < bytes.len() {
let b = bytes[i];
if b == b'/' && bytes.get(i + 1) == Some(&b'/') {
while i < bytes.len() && bytes[i] != b'\n' {
mask[i] = false;
i += 1;
}
continue;
}
if b == b'/' && bytes.get(i + 1) == Some(&b'*') {
i = mask_nested_block_comment(bytes, &mut mask, i);
continue;
}
if is_ident_start(b) {
if let Some((quote, hashes)) = raw_string_prefix(bytes, i) {
mask[i..quote].fill(false);
i = mask_raw_string(bytes, &mut mask, quote, hashes);
continue;
}
let (_, after) = read_ident(bytes, i).expect("ident start");
i = after;
continue;
}
if b == b'"' {
i = mask_quoted(bytes, &mut mask, i, b'"', false);
continue;
}
if b == b'\'' {
if char_literal_end(bytes, i).is_some() {
i = mask_quoted(bytes, &mut mask, i, b'\'', true);
} else {
i += 1;
}
continue;
}
i += 1;
}
mask
}

/// Mask a nestable block comment starting at `i` (`/*`). Returns the index just
/// past the outermost `*/`.
fn mask_nested_block_comment(bytes: &[u8], mask: &mut [bool], i: usize) -> usize {
let mut depth = 0usize;
let mut k = i;
while k < bytes.len() {
if bytes[k] == b'/' && bytes.get(k + 1) == Some(&b'*') {
depth += 1;
mask[k] = false;
mask[k + 1] = false;
k += 2;
continue;
}
if bytes[k] == b'*' && bytes.get(k + 1) == Some(&b'/') {
mask[k] = false;
mask[k + 1] = false;
k += 2;
depth -= 1;
if depth == 0 {
return k;
}
continue;
}
mask[k] = false;
k += 1;
}
bytes.len()
}

/// If a raw-string prefix (`r"`, `r##"`, `br"`, `br##"`) starts at `i`, return
/// the index of its opening quote and its hash count.
fn raw_string_prefix(bytes: &[u8], i: usize) -> Option<(usize, usize)> {
let mut j = i;
if bytes.get(j) == Some(&b'b') {
j += 1;
}
if bytes.get(j) != Some(&b'r') {
return None;
}
j += 1;
let hash_start = j;
while bytes.get(j) == Some(&b'#') {
j += 1;
}
if bytes.get(j) != Some(&b'"') {
return None;
}
Some((j, j - hash_start))
}

/// Mask a raw string whose opening quote is at `quote` and which closes on a `"`
/// followed by exactly `hashes` `#`.
fn mask_raw_string(bytes: &[u8], mask: &mut [bool], quote: usize, hashes: usize) -> usize {
mask[quote] = false;
let mut k = quote + 1;
while k < bytes.len() {
if bytes[k] == b'"' && closes_raw_string(bytes, k, hashes) {
let end = (k + hashes).min(bytes.len() - 1);
mask[k..=end].fill(false);
return (k + hashes + 1).min(bytes.len());
}
mask[k] = false;
k += 1;
}
bytes.len()
}

fn closes_raw_string(bytes: &[u8], quote: usize, hashes: usize) -> bool {
(1..=hashes).all(|n| bytes.get(quote + n) == Some(&b'#'))
}

/// Mask a `delim`-quoted literal starting at `start`, honouring `\` escapes.
///
/// `reset_at_newline` is `true` only for char literals: a Rust string may legally
/// span lines, while an unclosed `'` means the disambiguation misjudged a
/// lifetime, and failing open there is safer than masking the rest of the file.
fn mask_quoted(
bytes: &[u8],
mask: &mut [bool],
start: usize,
delim: u8,
reset_at_newline: bool,
) -> usize {
mask[start] = false;
let mut i = start + 1;
while i < bytes.len() {
let b = bytes[i];
if b == b'\n' && reset_at_newline {
return i;
}
mask[i] = false;
if b == b'\\' {
if i + 1 < bytes.len() {
mask[i + 1] = false;
}
i += 2;
continue;
}
i += 1;
if b == delim {
return i;
}
}
bytes.len()
}

/// Index just past the closing `'` when `i` opens a genuine char literal, or
/// `None` when it opens a LIFETIME or label.
///
/// `'\''` takes the escape branch; `'a'` is a literal because the byte after the
/// single character is the closing quote; `'a` in `&'a str` and `'static` are
/// lifetimes, so they open no state at all.
fn char_literal_end(bytes: &[u8], i: usize) -> Option<usize> {
if bytes.get(i + 1) == Some(&b'\\') {
let mut k = i + 2;
while k < bytes.len() && bytes[k] != b'\'' && bytes[k] != b'\n' {
k += 1;
}
return (bytes.get(k) == Some(&b'\'')).then_some(k + 1);
}
let rest = std::str::from_utf8(&bytes[i + 1..]).ok()?;
let ch = rest.chars().next()?;
let after = i + 1 + ch.len_utf8();
(bytes.get(after) == Some(&b'\'')).then_some(after + 1)
}

// ---------------------------------------------------------------------------
// Call side (D3): the `invoke('name')` call sites
// ---------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions crates/codegraph-resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod object_literal;
pub mod path_aliases;
pub mod pathutil;
pub mod resolver;
mod rust_lexical;
pub mod snapshot_context;
pub mod source_facts;
pub mod strip_comments;
Expand Down
Loading
Loading