Skip to content

Commit d1f41ed

Browse files
author
Eric Wheeler
committed
feat: enhance Rust tree-sitter parser with advanced language structures
- Add support for additional Rust language structures: - Enum definitions - Trait definitions - Impl trait for struct - Generic structs with lifetime parameters - Macro definitions - Module definitions - Union types - Closures - Match expressions - Where clauses - Async functions - Impl blocks with generic parameters - Complex trait bounds - Update tests to verify capture of these structures - Modify debug helper to use environment variables Signed-off-by: Eric Wheeler <[email protected]>
1 parent 419b185 commit d1f41ed

File tree

3 files changed

+342
-47
lines changed

3 files changed

+342
-47
lines changed

src/services/tree-sitter/__tests__/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import * as path from "path"
55
import Parser from "web-tree-sitter"
66
import tsxQuery from "../queries/tsx"
77

8-
// Global debug flag - set to 0 to disable debug logging
9-
export const DEBUG = 0
8+
// Global debug flag - read from environment variable or default to 0
9+
export const DEBUG = process.env.DEBUG ? parseInt(process.env.DEBUG, 10) : 0
1010

1111
// Debug function to conditionally log messages
1212
export const debugLog = (message: string, ...args: any[]) => {

src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.rust.test.ts

Lines changed: 249 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import { initializeTreeSitter, testParseSourceCodeDefinitions, inspectTreeStruct
1212
// - struct definitions
1313
// - method definitions (functions within a declaration list)
1414
// - function definitions
15+
// - enum definitions
16+
// - trait definitions
17+
// - impl trait for struct
18+
// - generic structs with lifetime parameters
1519
const sampleRustContent = `
1620
// Basic struct definition
1721
struct Point {
@@ -117,6 +121,190 @@ impl<'a, T> Container<'a, T> {
117121
}
118122
}
119123
}
124+
125+
// Macro definition
126+
macro_rules! say_hello {
127+
// Match a single name
128+
($name:expr) => {
129+
println!("Hello, {}!", $name);
130+
};
131+
// Match multiple names
132+
($($name:expr),*) => {
133+
$(
134+
println!("Hello, {}!", $name);
135+
)*
136+
};
137+
}
138+
139+
// Module definition
140+
mod math {
141+
// Constants
142+
pub const PI: f64 = 3.14159;
143+
144+
// Static variables
145+
pub static VERSION: &str = "1.0.0";
146+
147+
// Type alias
148+
pub type Number = f64;
149+
150+
// Functions within modules
151+
pub fn add(a: Number, b: Number) -> Number {
152+
a + b
153+
}
154+
155+
pub fn subtract(a: Number, b: Number) -> Number {
156+
a - b
157+
}
158+
}
159+
160+
// Union type
161+
union IntOrFloat {
162+
int_value: i32,
163+
float_value: f32,
164+
}
165+
166+
// Trait with associated types
167+
trait Iterator {
168+
// Associated type
169+
type Item;
170+
171+
// Method using associated type
172+
fn next(&mut self) -> Option<Self::Item>;
173+
174+
// Default implementation
175+
fn count(self) -> usize where Self: Sized {
176+
let mut count = 0;
177+
while let Some(_) = self.next() {
178+
count += 1;
179+
}
180+
count
181+
}
182+
}
183+
184+
// Advanced Rust language features for testing
185+
186+
// 1. Closures: Multi-line anonymous functions with captured environments
187+
fn use_closures() {
188+
let captured_value = 42;
189+
190+
// Simple closure
191+
let simple_closure = || {
192+
println!("Captured value: {}", captured_value);
193+
};
194+
195+
// Closure with parameters
196+
let add_closure = |a: i32, b: i32| -> i32 {
197+
let sum = a + b + captured_value;
198+
println!("Sum with captured value: {}", sum);
199+
sum
200+
};
201+
202+
// Using closures
203+
simple_closure();
204+
let result = add_closure(10, 20);
205+
}
206+
207+
// 2. Match Expressions: Complex pattern matching constructs
208+
fn complex_matching(value: Option<Result<Vec<i32>, String>>) {
209+
match value {
210+
Some(Ok(vec)) if vec.len() > 5 => {
211+
println!("Got a vector with more than 5 elements");
212+
for item in vec {
213+
println!("Item: {}", item);
214+
}
215+
},
216+
Some(Ok(vec)) => {
217+
println!("Got a vector with {} elements", vec.len());
218+
},
219+
Some(Err(e)) => {
220+
println!("Got an error: {}", e);
221+
},
222+
None => {
223+
println!("Got nothing");
224+
}
225+
}
226+
}
227+
228+
// 3. Where Clauses: Type constraints on generic parameters
229+
fn print_sorted<T>(collection: &[T])
230+
where
231+
T: std::fmt::Debug + Ord + Clone,
232+
{
233+
let mut sorted = collection.to_vec();
234+
sorted.sort();
235+
println!("Sorted collection: {:?}", sorted);
236+
}
237+
238+
// 4. Attribute Macros: Annotations that modify behavior
239+
#[derive(Debug, Clone, PartialEq)]
240+
struct AttributeExample {
241+
field1: String,
242+
field2: i32,
243+
}
244+
245+
#[cfg(test)]
246+
mod test_module {
247+
#[test]
248+
fn test_example() {
249+
assert_eq!(2 + 2, 4);
250+
}
251+
}
252+
253+
// 5. Procedural Macros (simulated, as they require separate crates)
254+
// This is a placeholder to represent a proc macro
255+
// In real code, this would be in a separate crate with #[proc_macro]
256+
fn custom_derive_macro() {
257+
// Implementation would generate code at compile time
258+
}
259+
260+
// 6. Async Functions and Blocks: Asynchronous code constructs
261+
async fn fetch_data(url: &str) -> Result<String, String> {
262+
// Simulated async operation
263+
println!("Fetching data from {}", url);
264+
265+
// Async block
266+
let result = async {
267+
// Simulated async work
268+
Ok("Response data".to_string())
269+
}.await;
270+
271+
result
272+
}
273+
274+
// 7. Impl Blocks with Generic Parameters: Implementation with complex type parameters
275+
struct GenericContainer<T, U> {
276+
first: T,
277+
second: U,
278+
}
279+
280+
impl<T, U> GenericContainer<T, U>
281+
where
282+
T: std::fmt::Display,
283+
U: std::fmt::Debug,
284+
{
285+
fn new(first: T, second: U) -> Self {
286+
GenericContainer { first, second }
287+
}
288+
289+
fn display(&self) {
290+
println!("First: {}, Second: {:?}", self.first, self.second);
291+
}
292+
}
293+
294+
// 8. Complex Trait Bounds: Trait bounds using + operator or where clauses
295+
trait Processor<T> {
296+
fn process(&self, item: T) -> T;
297+
}
298+
299+
fn process_items<T, P>(processor: P, items: Vec<T>) -> Vec<T>
300+
where
301+
P: Processor<T> + Clone,
302+
T: Clone + std::fmt::Debug + 'static,
303+
{
304+
items.into_iter()
305+
.map(|item| processor.process(item))
306+
.collect()
307+
}
120308
`
121309

122310
// Rust test options
@@ -218,54 +406,70 @@ describe("parseSourceCodeDefinitionsForFile with Rust", () => {
218406
const result = await testParseSourceCodeDefinitions("/test/file.rs", sampleRustContent, rustOptions)
219407
const resultLines = result?.split("\n") || []
220408

221-
// We're not testing specific captures here since the current query might not capture all these structures
222-
// Instead, we're verifying that the parser doesn't crash with more complex Rust code
223-
224-
// The test passes if parsing completes without errors
409+
// Now we test specific captures for all supported structures
225410
expect(result).toBeTruthy()
226411

227-
// If the parser is enhanced in the future to capture these structures,
228-
// we can add more specific assertions here:
229-
// - enum definitions
230-
// - trait definitions
231-
// - impl trait for struct
232-
// - generic structs with lifetime parameters
233-
})
412+
// Test enum definitions
413+
expect(resultLines.some((line) => line.includes("enum Status"))).toBe(true)
414+
415+
// Test trait definitions
416+
expect(resultLines.some((line) => line.includes("trait Drawable"))).toBe(true)
417+
418+
// Test impl trait for struct
419+
expect(resultLines.some((line) => line.includes("impl Drawable for Rectangle"))).toBe(true)
420+
421+
// Test generic structs with lifetime parameters
422+
expect(resultLines.some((line) => line.includes("struct Container<'a, T>"))).toBe(true)
423+
424+
// Test macro definitions
425+
expect(resultLines.some((line) => line.includes("macro_rules! say_hello"))).toBe(true)
426+
427+
// Test module definitions
428+
expect(resultLines.some((line) => line.includes("mod math"))).toBe(true)
429+
430+
// Test union types
431+
expect(resultLines.some((line) => line.includes("union IntOrFloat"))).toBe(true)
234432

235-
// Debug test that can be enabled for diagnosing issues
236-
it.skip("should debug Rust tree structure directly", async () => {
237-
jest.unmock("fs/promises")
238-
239-
// Initialize tree-sitter
240-
const TreeSitter = await initializeTreeSitter()
241-
242-
// Create parser and load Rust language
243-
const parser = new TreeSitter()
244-
const wasmPath = path.join(process.cwd(), "dist/tree-sitter-rust.wasm")
245-
const rustLang = await TreeSitter.Language.load(wasmPath)
246-
parser.setLanguage(rustLang)
247-
248-
// Parse the content
249-
const tree = parser.parse(sampleRustContent)
250-
251-
// Create the query
252-
const query = rustLang.query(rustQuery)
253-
254-
// Execute the query
255-
const captures = query.captures(tree.rootNode)
256-
257-
// Log the results for debugging
258-
console.log(
259-
"Captures:",
260-
captures.map((c: { node: Parser.SyntaxNode; name: string }) => ({
261-
name: c.name,
262-
text: c.node.text,
263-
type: c.node.type,
264-
startRow: c.node.startPosition.row,
265-
startCol: c.node.startPosition.column,
266-
endRow: c.node.endPosition.row,
267-
endCol: c.node.endPosition.column,
268-
})),
433+
// Test trait with associated types
434+
expect(resultLines.some((line) => line.includes("trait Iterator"))).toBe(true)
435+
436+
// Test advanced Rust language features
437+
// 1. Closures
438+
expect(
439+
resultLines.some(
440+
(line) =>
441+
line.includes("let simple_closure") ||
442+
line.includes("let add_closure") ||
443+
line.includes("closure_expression"),
444+
),
445+
).toBe(true)
446+
447+
// 2. Match expressions
448+
expect(resultLines.some((line) => line.includes("match value") || line.includes("match_expression"))).toBe(true)
449+
450+
// 3. Functions with where clauses
451+
expect(resultLines.some((line) => line.includes("fn print_sorted") || line.includes("where_clause"))).toBe(true)
452+
453+
// 4. Attribute macros - Note: These might not be directly captured by the current query
454+
// Instead, we check for the struct that has the attribute
455+
expect(resultLines.some((line) => line.includes("struct AttributeExample"))).toBe(true)
456+
457+
// 5. Async functions
458+
expect(resultLines.some((line) => line.includes("async fn fetch_data"))).toBe(true)
459+
460+
// 6. Impl blocks with generic parameters
461+
expect(resultLines.some((line) => line.includes("impl<T, U> GenericContainer"))).toBe(true)
462+
463+
// 7. Functions with complex trait bounds
464+
expect(resultLines.some((line) => line.includes("fn process_items") || line.includes("trait_bounds"))).toBe(
465+
true,
269466
)
467+
468+
// Note: The following structures are nested inside modules and might not be captured directly
469+
// - Type aliases (type Number)
470+
// - Constants (const PI)
471+
// - Static variables (static VERSION)
472+
// - Associated types (type Item)
473+
// These would require more complex query patterns or post-processing to extract
270474
})
271475
})

0 commit comments

Comments
 (0)