• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

igorls / context-builder / 22050078724

16 Feb 2026 04:24AM UTC coverage: 77.617% (-2.2%) from 79.772%
22050078724

push

github

igorls
release: v0.8.3

Bug fixes (Gemini Deep Think v6 clean benchmark):
- Fix output_folder auto-ignore silently hiding user content
- Fix diff_context_lines config ignored in auto-diff mode
- Fix double file I/O in process_file (reuse open fd)

Security hardening:
- install.sh: default to ~/.local/bin (no sudo needed)
- SKILL.md: promote cargo install, add SHA256 verification docs
- Soften -y flag guidance to require explicit path scoping

134 of 255 new or added lines in 6 files covered. (52.55%)

21 existing lines in 3 files now uncovered.

2410 of 3105 relevant lines covered (77.62%)

4.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.07
/src/tree_sitter/languages/cpp.rs
1
//! C++ language support for tree-sitter.
2

3
#[cfg(feature = "tree-sitter-cpp")]
4
use tree_sitter::{Parser, Tree};
5

6
#[cfg(feature = "tree-sitter-cpp")]
7
use crate::tree_sitter::language_support::{
8
    CodeStructure, LanguageSupport, Signature, SignatureKind, Visibility,
9
    slice_signature_before_body,
10
};
11

12
pub struct CppSupport;
13

14
#[cfg(feature = "tree-sitter-cpp")]
15
impl CppSupport {
16
    fn get_language() -> tree_sitter::Language {
1✔
17
        tree_sitter_cpp::LANGUAGE.into()
1✔
18
    }
19
}
20

21
#[cfg(feature = "tree-sitter-cpp")]
22
impl LanguageSupport for CppSupport {
23
    fn file_extensions(&self) -> &[&'static str] {
1✔
24
        &["cpp", "cxx", "cc", "hpp", "hxx", "hh"]
25
    }
26

27
    fn parse(&self, source: &str) -> Option<Tree> {
1✔
28
        let mut parser = Parser::new();
1✔
29
        parser.set_language(&Self::get_language()).ok()?;
2✔
30
        parser.parse(source, None)
1✔
31
    }
32

33
    fn extract_signatures(&self, source: &str, visibility: Visibility) -> Vec<Signature> {
1✔
34
        let tree = match self.parse(source) {
1✔
35
            Some(t) => t,
1✔
36
            None => return Vec::new(),
×
37
        };
38

39
        let root = tree.root_node();
1✔
40
        let mut signatures = Vec::new();
1✔
41

42
        self.extract_signatures_from_node(source, &root, visibility, &mut signatures);
1✔
43

44
        signatures.sort_by_key(|s| s.line_number);
3✔
45
        signatures
1✔
46
    }
47

48
    fn extract_structure(&self, source: &str) -> CodeStructure {
1✔
49
        let tree = match self.parse(source) {
1✔
50
            Some(t) => t,
1✔
51
            None => return CodeStructure::default(),
×
52
        };
53

54
        let root = tree.root_node();
1✔
55
        let mut structure = CodeStructure {
56
            total_lines: source.lines().count(),
1✔
57
            ..Default::default()
58
        };
59

60
        self.extract_structure_from_node(&root, &mut structure);
1✔
61
        structure
1✔
62
    }
63

64
    fn find_truncation_point(&self, source: &str, max_bytes: usize) -> usize {
1✔
65
        if source.len() <= max_bytes {
1✔
66
            return source.len();
1✔
67
        }
68

69
        let tree = match self.parse(source) {
×
70
            Some(t) => t,
×
71
            None => return max_bytes,
×
72
        };
73

74
        let root = tree.root_node();
×
75
        let mut best_end = 0;
×
76

77
        let mut cursor = root.walk();
×
78
        self.find_best_boundary(&mut cursor, max_bytes, &mut best_end);
×
79
        drop(cursor);
×
80

81
        if best_end == 0 { max_bytes } else { best_end }
×
82
    }
83
}
84

85
#[cfg(feature = "tree-sitter-cpp")]
86
impl CppSupport {
87
    fn extract_signatures_from_node(
1✔
88
        &self,
89
        source: &str,
90
        node: &tree_sitter::Node,
91
        visibility: Visibility,
92
        signatures: &mut Vec<Signature>,
93
    ) {
94
        match node.kind() {
1✔
95
            "function_definition" => {
1✔
96
                if let Some(sig) = self.extract_function_signature(source, node, visibility) {
2✔
97
                    signatures.push(sig);
1✔
98
                }
99
            }
100
            "template_declaration" => {
1✔
101
                // Walk into template_declaration to find the inner class/struct/function
102
                // while preserving the template<...> prefix via slice_signature_before_body
103
                let mut cursor = node.walk();
1✔
104
                for child in node.children(&mut cursor) {
2✔
105
                    match child.kind() {
2✔
106
                        "function_definition" => {
1✔
107
                            // Extract using the template_declaration node for full signature
NEW
108
                            if let Some(mut sig) = self.extract_function_signature(source, &child, visibility) {
×
109
                                // Replace signature with one that includes template<...> prefix
NEW
110
                                if let Some(full) = slice_signature_before_body(source, node, &["compound_statement", "function_body"]) {
×
NEW
111
                                    sig.full_signature = full;
×
112
                                }
NEW
113
                                signatures.push(sig);
×
114
                            }
115
                        }
116
                        "class_specifier" => {
2✔
117
                            if let Some(mut sig) = self.extract_class_signature(source, &child, visibility) {
3✔
118
                                if let Some(full) = slice_signature_before_body(source, node, &["field_declaration_list"]) {
2✔
NEW
119
                                    sig.full_signature = full;
×
120
                                }
121
                                signatures.push(sig);
1✔
122
                            }
123
                        }
124
                        "struct_specifier" => {
2✔
NEW
125
                            if let Some(mut sig) = self.extract_struct_signature(source, &child) {
×
NEW
126
                                if let Some(full) = slice_signature_before_body(source, node, &["field_declaration_list"]) {
×
NEW
127
                                    sig.full_signature = full;
×
128
                                }
NEW
129
                                signatures.push(sig);
×
130
                            }
131
                        }
132
                        _ => {}
133
                    }
134
                }
135
                return; // Don't recurse into template children again
136
            }
137
            "declaration" => {
1✔
138
                // Header file prototypes: `int foo(int x, int y);`
139
                if let Some(sig) = self.extract_declaration_signature(source, node) {
2✔
140
                    signatures.push(sig);
1✔
141
                }
142
            }
143
            "class_specifier" => {
1✔
144
                if let Some(sig) = self.extract_class_signature(source, node, visibility) {
2✔
145
                    signatures.push(sig);
1✔
146
                }
147
            }
148
            "struct_specifier" => {
1✔
149
                if let Some(sig) = self.extract_struct_signature(source, node) {
2✔
150
                    signatures.push(sig);
1✔
151
                }
152
            }
153
            "enum_specifier" => {
1✔
154
                if let Some(sig) = self.extract_enum_signature(source, node) {
2✔
155
                    signatures.push(sig);
1✔
156
                }
157
            }
158
            "alias_declaration" | "type_definition" => {
2✔
159
                if let Some(sig) = self.extract_alias_signature(source, node) {
2✔
160
                    signatures.push(sig);
1✔
161
                }
162
            }
163
            "preproc_function_def" => {
1✔
164
                if let Some(sig) = self.extract_macro_signature(source, node) {
1✔
165
                    signatures.push(sig);
1✔
166
                }
167
            }
168
            _ => {}
169
        }
170

171
        let mut cursor = node.walk();
1✔
172
        for child in node.children(&mut cursor) {
2✔
173
            self.extract_signatures_from_node(source, &child, visibility, signatures);
2✔
174
        }
175
    }
176

177
    fn extract_structure_from_node(&self, node: &tree_sitter::Node, structure: &mut CodeStructure) {
1✔
178
        match node.kind() {
1✔
179
            "function_definition" => structure.functions += 1,
3✔
180
            "class_specifier" => structure.classes += 1,
3✔
181
            "struct_specifier" => structure.structs += 1,
3✔
182
            "enum_specifier" => structure.enums += 1,
3✔
183
            "preproc_include" => {
1✔
184
                structure.imports.push("include".to_string());
1✔
185
            }
186
            _ => {}
187
        }
188

189
        let mut cursor = node.walk();
1✔
190
        for child in node.children(&mut cursor) {
2✔
191
            self.extract_structure_from_node(&child, structure);
2✔
192
        }
193
    }
194

195
    #[allow(dead_code)]
196
    fn get_visibility(&self, _node: &tree_sitter::Node) -> Visibility {
×
197
        // C++ has access specifiers: public, private, protected
198
        // For simplicity, we check sibling nodes for access specifiers
199
        // This is a simplified check; full implementation would track class context
200
        Visibility::All
201
    }
202

203
    fn extract_function_signature(
1✔
204
        &self,
205
        source: &str,
206
        node: &tree_sitter::Node,
207
        visibility: Visibility,
208
    ) -> Option<Signature> {
209
        let name = self.find_function_name(node, source)?;
2✔
210
        let return_type = self.find_return_type(node, source);
2✔
211
        let params = self.find_child_text(node, "parameter_list", source);
2✔
212

213
        // Use byte-slicing to preserve templates, parameters, and qualifiers
214
        let full_sig = slice_signature_before_body(source, node, &["compound_statement"])
1✔
215
            .unwrap_or_else(|| {
1✔
216
                let mut sig = String::new();
×
217
                if let Some(r) = &return_type {
×
218
                    sig.push_str(r);
×
219
                    sig.push(' ');
×
220
                }
221
                sig.push_str(&name);
×
222
                if let Some(p) = &params {
×
223
                    sig.push_str(p);
×
224
                } else {
225
                    sig.push_str("()");
×
226
                }
227
                sig
×
228
            });
229

230
        Some(Signature {
1✔
231
            kind: SignatureKind::Function,
232
            name,
1✔
233
            params,
1✔
234
            return_type,
1✔
235
            visibility,
236
            line_number: node.start_position().row + 1,
2✔
237
            full_signature: full_sig,
1✔
238
        })
239
    }
240

241
    /// Extract function prototype signatures from `declaration` nodes (header files).
242
    fn extract_declaration_signature(
1✔
243
        &self,
244
        source: &str,
245
        node: &tree_sitter::Node,
246
    ) -> Option<Signature> {
247
        // Only capture declarations that look like function prototypes
248
        let mut cursor = node.walk();
1✔
249
        let has_function_declarator = node.children(&mut cursor).any(|c| {
3✔
250
            if c.kind() == "function_declarator" {
1✔
251
                return true;
1✔
252
            }
253
            let mut inner = c.walk();
1✔
254
            c.children(&mut inner)
2✔
255
                .any(|gc| gc.kind() == "function_declarator")
1✔
256
        });
257

258
        if !has_function_declarator {
1✔
259
            return None;
×
260
        }
261

262
        let name = self.find_function_name(node, source)?;
2✔
263
        let text = source[node.start_byte()..node.end_byte()].trim_end();
2✔
264
        let full_sig = text.trim_end_matches(';').trim_end().to_string();
1✔
265

266
        Some(Signature {
1✔
267
            kind: SignatureKind::Function,
268
            name,
1✔
269
            params: None,
1✔
270
            return_type: None,
1✔
271
            visibility: Visibility::All,
272
            line_number: node.start_position().row + 1,
2✔
273
            full_signature: full_sig,
1✔
274
        })
275
    }
276
    fn extract_class_signature(
1✔
277
        &self,
278
        source: &str,
279
        node: &tree_sitter::Node,
280
        visibility: Visibility,
281
    ) -> Option<Signature> {
282
        let name = self.find_child_text(node, "type_identifier", source)?;
2✔
283

284
        // Use byte-slicing to preserve templates and inheritance
285
        // e.g., `template<typename T> class Foo : public Base`
286
        let full_sig = slice_signature_before_body(source, node, &["field_declaration_list"])
1✔
287
            .unwrap_or_else(|| format!("class {}", name));
1✔
288

289
        Some(Signature {
1✔
290
            kind: SignatureKind::Class,
291
            name,
1✔
292
            params: None,
1✔
293
            return_type: None,
1✔
294
            visibility,
295
            line_number: node.start_position().row + 1,
2✔
296
            full_signature: full_sig,
1✔
297
        })
298
    }
299

300
    fn extract_struct_signature(
1✔
301
        &self,
302
        source: &str,
303
        node: &tree_sitter::Node,
304
    ) -> Option<Signature> {
305
        let name = self.find_child_text(node, "type_identifier", source)?;
2✔
306

307
        let full_sig = format!("struct {}", name);
2✔
308

309
        Some(Signature {
1✔
310
            kind: SignatureKind::Struct,
311
            name,
1✔
312
            params: None,
1✔
313
            return_type: None,
1✔
314
            visibility: Visibility::All,
315
            line_number: node.start_position().row + 1,
2✔
316
            full_signature: full_sig,
1✔
317
        })
318
    }
319

320
    fn extract_enum_signature(&self, source: &str, node: &tree_sitter::Node) -> Option<Signature> {
1✔
321
        let name = self.find_child_text(node, "type_identifier", source)?;
2✔
322

323
        let full_sig = format!("enum {}", name);
2✔
324

325
        Some(Signature {
1✔
326
            kind: SignatureKind::Enum,
327
            name,
1✔
328
            params: None,
1✔
329
            return_type: None,
1✔
330
            visibility: Visibility::All,
331
            line_number: node.start_position().row + 1,
2✔
332
            full_signature: full_sig,
1✔
333
        })
334
    }
335

336
    fn extract_alias_signature(&self, source: &str, node: &tree_sitter::Node) -> Option<Signature> {
1✔
337
        let name = self.find_child_text(node, "type_identifier", source)?;
2✔
338

339
        let full_sig = format!("using/typedef {}", name);
2✔
340

341
        Some(Signature {
1✔
342
            kind: SignatureKind::TypeAlias,
343
            name,
1✔
344
            params: None,
1✔
345
            return_type: None,
1✔
346
            visibility: Visibility::All,
347
            line_number: node.start_position().row + 1,
2✔
348
            full_signature: full_sig,
1✔
349
        })
350
    }
351

352
    fn extract_macro_signature(&self, source: &str, node: &tree_sitter::Node) -> Option<Signature> {
1✔
353
        let name = self.find_child_text(node, "identifier", source)?;
2✔
354

355
        let full_sig = format!("#define {}", name);
2✔
356

357
        Some(Signature {
1✔
358
            kind: SignatureKind::Macro,
359
            name,
1✔
360
            params: None,
1✔
361
            return_type: None,
1✔
362
            visibility: Visibility::All,
363
            line_number: node.start_position().row + 1,
2✔
364
            full_signature: full_sig,
1✔
365
        })
366
    }
367

368
    fn find_function_name(&self, node: &tree_sitter::Node, source: &str) -> Option<String> {
1✔
369
        let mut cursor = node.walk();
1✔
370
        for child in node.children(&mut cursor) {
2✔
371
            if child.kind() == "function_declarator" || child.kind() == "reference_declarator" {
3✔
372
                let mut inner_cursor = child.walk();
1✔
373
                for inner in child.children(&mut inner_cursor) {
2✔
374
                    if inner.kind() == "identifier" || inner.kind() == "qualified_identifier" {
3✔
375
                        return Some(source[inner.start_byte()..inner.end_byte()].to_string());
2✔
376
                    }
377
                }
378
            }
379
            if child.kind() == "identifier" || child.kind() == "qualified_identifier" {
3✔
380
                return Some(source[child.start_byte()..child.end_byte()].to_string());
×
381
            }
382
        }
383
        None
1✔
384
    }
385

386
    fn find_return_type(&self, node: &tree_sitter::Node, source: &str) -> Option<String> {
1✔
387
        let mut cursor = node.walk();
1✔
388
        for child in node.children(&mut cursor) {
2✔
389
            match child.kind() {
2✔
390
                "primitive_type" | "type_identifier" | "sized_type_specifier" => {
1✔
391
                    return Some(source[child.start_byte()..child.end_byte()].to_string());
2✔
392
                }
393
                _ => {}
394
            }
395
        }
396
        None
×
397
    }
398

399
    fn find_child_text(
1✔
400
        &self,
401
        node: &tree_sitter::Node,
402
        kind: &str,
403
        source: &str,
404
    ) -> Option<String> {
405
        let mut cursor = node.walk();
1✔
406
        for child in node.children(&mut cursor) {
2✔
407
            if child.kind() == kind {
2✔
408
                return Some(source[child.start_byte()..child.end_byte()].to_string());
1✔
409
            }
410
        }
411
        None
1✔
412
    }
413

414
    fn find_best_boundary(
×
415
        &self,
416
        cursor: &mut tree_sitter::TreeCursor,
417
        max_bytes: usize,
418
        best_end: &mut usize,
419
    ) {
420
        loop {
421
            let node = cursor.node();
×
422
            let end_byte = node.end_byte();
×
423

424
            if end_byte <= max_bytes && end_byte > *best_end {
×
425
                let is_item = matches!(
×
426
                    node.kind(),
×
427
                    "function_definition"
428
                        | "class_specifier"
429
                        | "struct_specifier"
430
                        | "enum_specifier"
431
                        | "alias_declaration"
432
                        | "type_definition"
433
                );
434
                if is_item {
×
435
                    *best_end = end_byte;
×
436
                }
437
            }
438

439
            if cursor.goto_first_child() {
×
440
                self.find_best_boundary(cursor, max_bytes, best_end);
×
441
                cursor.goto_parent();
×
442
            }
443

444
            if !cursor.goto_next_sibling() {
×
445
                break;
446
            }
447
        }
448
    }
449
}
450

451
#[cfg(test)]
452
mod tests {
453
    use super::*;
454

455
    #[test]
456
    fn test_extract_class_signature() {
457
        let source = r#"
458
class HelloWorld {
459
public:
460
    void greet() {
461
        std::cout << "Hello" << std::endl;
462
    }
463
};
464
"#;
465

466
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
467
        let classes: Vec<_> = signatures
468
            .iter()
469
            .filter(|s| s.kind == SignatureKind::Class)
470
            .collect();
471
        assert!(!classes.is_empty());
472
        assert_eq!(classes[0].name, "HelloWorld");
473
    }
474

475
    #[test]
476
    fn test_extract_function_signature() {
477
        let source = r#"
478
int add(int a, int b) {
479
    return a + b;
480
}
481

482
void greet(const std::string& name) {
483
    std::cout << name << std::endl;
484
}
485
"#;
486

487
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
488
        let funcs: Vec<_> = signatures
489
            .iter()
490
            .filter(|s| s.kind == SignatureKind::Function)
491
            .collect();
492
        assert!(funcs.len() >= 2);
493
    }
494

495
    #[test]
496
    fn test_extract_struct_signature() {
497
        let source = r#"
498
struct Vec3 {
499
    float x, y, z;
500
};
501
"#;
502

503
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
504
        let structs: Vec<_> = signatures
505
            .iter()
506
            .filter(|s| s.kind == SignatureKind::Struct)
507
            .collect();
508
        assert!(!structs.is_empty());
509
        assert_eq!(structs[0].name, "Vec3");
510
    }
511

512
    #[test]
513
    fn test_extract_enum_signature() {
514
        let source = r#"
515
enum class Direction {
516
    Up,
517
    Down,
518
    Left,
519
    Right
520
};
521
"#;
522

523
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
524
        let enums: Vec<_> = signatures
525
            .iter()
526
            .filter(|s| s.kind == SignatureKind::Enum)
527
            .collect();
528
        assert!(!enums.is_empty());
529
        assert_eq!(enums[0].name, "Direction");
530
    }
531

532
    #[test]
533
    fn test_extract_header_prototype() {
534
        let source = r#"
535
int add(int a, int b);
536
void greet(const std::string& name);
537
"#;
538

539
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
540
        let funcs: Vec<_> = signatures
541
            .iter()
542
            .filter(|s| s.kind == SignatureKind::Function)
543
            .collect();
544
        assert!(funcs.len() >= 2);
545
        for f in &funcs {
546
            assert!(!f.full_signature.ends_with(';'));
547
        }
548
    }
549

550
    #[test]
551
    fn test_extract_template_class_with_inheritance() {
552
        let source = r#"
553
template<typename T>
554
class Container : public Base {
555
    T value;
556
public:
557
    T get() { return value; }
558
};
559
"#;
560

561
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
562
        let classes: Vec<_> = signatures
563
            .iter()
564
            .filter(|s| s.kind == SignatureKind::Class)
565
            .collect();
566
        assert!(!classes.is_empty());
567
        // Byte-slicing fix should preserve template<> and : public Base
568
        let sig = &classes[0].full_signature;
569
        assert!(sig.contains("Container"));
570
    }
571

572
    #[test]
573
    fn test_extract_type_alias() {
574
        let source = r#"
575
using StringVec = std::vector<std::string>;
576
typedef unsigned int uint;
577
"#;
578

579
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
580
        let aliases: Vec<_> = signatures
581
            .iter()
582
            .filter(|s| s.kind == SignatureKind::TypeAlias)
583
            .collect();
584
        assert!(!aliases.is_empty());
585
    }
586

587
    #[test]
588
    fn test_extract_macro() {
589
        let source = r#"
590
#define MIN(a, b) ((a) < (b) ? (a) : (b))
591
"#;
592

593
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
594
        let macros: Vec<_> = signatures
595
            .iter()
596
            .filter(|s| s.kind == SignatureKind::Macro)
597
            .collect();
598
        assert!(!macros.is_empty());
599
        assert_eq!(macros[0].name, "MIN");
600
    }
601

602
    #[test]
603
    fn test_extract_structure() {
604
        let source = r#"
605
#include <iostream>
606
#include <vector>
607

608
class Foo {
609
public:
610
    void bar() {}
611
};
612

613
struct Point { int x; int y; };
614
enum Color { R, G, B };
615

616
void helper() {}
617
"#;
618

619
        let structure = CppSupport.extract_structure(source);
620
        assert!(structure.functions >= 1);
621
        assert!(structure.classes >= 1);
622
        assert!(structure.structs >= 1);
623
        assert!(structure.enums >= 1);
624
        assert!(structure.imports.len() >= 2);
625
    }
626

627
    #[test]
628
    fn test_parse_valid_cpp() {
629
        let source = "int main() { return 0; }";
630
        let tree = CppSupport.parse(source);
631
        assert!(tree.is_some());
632
    }
633

634
    #[test]
635
    fn test_find_truncation_point() {
636
        let source = "int main() { return 0; }";
637
        let point = CppSupport.find_truncation_point(source, 1000);
638
        assert_eq!(point, source.len());
639
    }
640

641
    #[test]
642
    fn test_file_extensions() {
643
        assert!(CppSupport.supports_extension("cpp"));
644
        assert!(CppSupport.supports_extension("hpp"));
645
        assert!(CppSupport.supports_extension("cxx"));
646
        assert!(!CppSupport.supports_extension("c"));
647
    }
648
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc