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

igorls / context-builder / 26983714325

04 Jun 2026 10:36PM UTC coverage: 78.514% (+0.7%) from 77.785%
26983714325

Pull #1

github

web-flow
Merge 29e37d251 into 87657bf69
Pull Request #1: v0.9.0 — Trustworthy Output: accurate tokens, honest features, pipe output

231 of 265 new or added lines in 12 files covered. (87.17%)

25 existing lines in 3 files now uncovered.

2536 of 3230 relevant lines covered (78.51%)

4.22 hits per line

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

79.2
/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
108
                            if let Some(mut sig) =
×
109
                                self.extract_function_signature(source, &child, visibility)
110
                            {
111
                                // Replace signature with one that includes template<...> prefix
112
                                if let Some(full) = slice_signature_before_body(
113
                                    source,
114
                                    node,
115
                                    &["compound_statement", "function_body"],
116
                                ) {
117
                                    sig.full_signature = full;
×
118
                                }
119
                                signatures.push(sig);
×
120
                            }
121
                        }
122
                        "class_specifier" => {
2✔
123
                            if let Some(mut sig) =
2✔
124
                                self.extract_class_signature(source, &child, visibility)
125
                            {
126
                                if let Some(full) = slice_signature_before_body(
127
                                    source,
128
                                    node,
129
                                    &["field_declaration_list"],
130
                                ) {
131
                                    sig.full_signature = full;
×
132
                                }
133
                                signatures.push(sig);
1✔
134
                            }
135
                        }
136
                        "struct_specifier" => {
2✔
137
                            if let Some(mut sig) = self.extract_struct_signature(source, &child) {
×
138
                                if let Some(full) = slice_signature_before_body(
139
                                    source,
140
                                    node,
141
                                    &["field_declaration_list"],
142
                                ) {
143
                                    sig.full_signature = full;
×
144
                                }
145
                                signatures.push(sig);
×
146
                            }
147
                        }
148
                        _ => {}
149
                    }
150
                }
151
                return; // Don't recurse into template children again
152
            }
153
            "declaration" => {
1✔
154
                // Header file prototypes: `int foo(int x, int y);`
155
                if let Some(sig) = self.extract_declaration_signature(source, node) {
2✔
156
                    signatures.push(sig);
1✔
157
                }
158
            }
159
            "class_specifier" => {
1✔
160
                if let Some(sig) = self.extract_class_signature(source, node, visibility) {
2✔
161
                    signatures.push(sig);
1✔
162
                }
163
            }
164
            "struct_specifier" => {
1✔
165
                if let Some(sig) = self.extract_struct_signature(source, node) {
2✔
166
                    signatures.push(sig);
1✔
167
                }
168
            }
169
            "enum_specifier" => {
1✔
170
                if let Some(sig) = self.extract_enum_signature(source, node) {
2✔
171
                    signatures.push(sig);
1✔
172
                }
173
            }
174
            "alias_declaration" | "type_definition" => {
2✔
175
                if let Some(sig) = self.extract_alias_signature(source, node) {
2✔
176
                    signatures.push(sig);
1✔
177
                }
178
            }
179
            "preproc_function_def" => {
1✔
180
                if let Some(sig) = self.extract_macro_signature(source, node) {
1✔
181
                    signatures.push(sig);
1✔
182
                }
183
            }
184
            _ => {}
185
        }
186

187
        let mut cursor = node.walk();
1✔
188
        for child in node.children(&mut cursor) {
2✔
189
            self.extract_signatures_from_node(source, &child, visibility, signatures);
2✔
190
        }
191
    }
192

193
    fn extract_structure_from_node(&self, node: &tree_sitter::Node, structure: &mut CodeStructure) {
1✔
194
        match node.kind() {
1✔
195
            "function_definition" => structure.functions += 1,
3✔
196
            "class_specifier" => structure.classes += 1,
3✔
197
            "struct_specifier" => structure.structs += 1,
3✔
198
            "enum_specifier" => structure.enums += 1,
3✔
199
            "preproc_include" => {
1✔
200
                structure.imports.push("include".to_string());
1✔
201
            }
202
            _ => {}
203
        }
204

205
        let mut cursor = node.walk();
1✔
206
        for child in node.children(&mut cursor) {
2✔
207
            self.extract_structure_from_node(&child, structure);
2✔
208
        }
209
    }
210

211
    #[allow(dead_code)]
212
    fn get_visibility(&self, _node: &tree_sitter::Node) -> Visibility {
×
213
        // C++ has access specifiers: public, private, protected
214
        // For simplicity, we check sibling nodes for access specifiers
215
        // This is a simplified check; full implementation would track class context
216
        Visibility::All
217
    }
218

219
    fn extract_function_signature(
1✔
220
        &self,
221
        source: &str,
222
        node: &tree_sitter::Node,
223
        visibility: Visibility,
224
    ) -> Option<Signature> {
225
        let name = self.find_function_name(node, source)?;
2✔
226
        let return_type = self.find_return_type(node, source);
2✔
227
        let params = self.find_child_text(node, "parameter_list", source);
2✔
228

229
        // Use byte-slicing to preserve templates, parameters, and qualifiers
230
        let full_sig = slice_signature_before_body(source, node, &["compound_statement"])
1✔
231
            .unwrap_or_else(|| {
1✔
232
                let mut sig = String::new();
×
233
                if let Some(r) = &return_type {
×
234
                    sig.push_str(r);
×
235
                    sig.push(' ');
×
236
                }
237
                sig.push_str(&name);
×
238
                if let Some(p) = &params {
×
239
                    sig.push_str(p);
×
240
                } else {
241
                    sig.push_str("()");
×
242
                }
243
                sig
×
244
            });
245

246
        Some(Signature {
1✔
247
            kind: SignatureKind::Function,
248
            name,
1✔
249
            params,
1✔
250
            return_type,
1✔
251
            visibility,
252
            line_number: node.start_position().row + 1,
2✔
253
            full_signature: full_sig,
1✔
254
        })
255
    }
256

257
    /// Extract function prototype signatures from `declaration` nodes (header files).
258
    fn extract_declaration_signature(
1✔
259
        &self,
260
        source: &str,
261
        node: &tree_sitter::Node,
262
    ) -> Option<Signature> {
263
        // Only capture declarations that look like function prototypes
264
        let mut cursor = node.walk();
1✔
265
        let has_function_declarator = node.children(&mut cursor).any(|c| {
3✔
266
            if c.kind() == "function_declarator" {
1✔
267
                return true;
1✔
268
            }
269
            let mut inner = c.walk();
1✔
270
            c.children(&mut inner)
2✔
271
                .any(|gc| gc.kind() == "function_declarator")
1✔
272
        });
273

274
        if !has_function_declarator {
1✔
275
            return None;
×
276
        }
277

278
        let name = self.find_function_name(node, source)?;
2✔
279
        let text = source[node.start_byte()..node.end_byte()].trim_end();
2✔
280
        let full_sig = text.trim_end_matches(';').trim_end().to_string();
1✔
281

282
        Some(Signature {
1✔
283
            kind: SignatureKind::Function,
284
            name,
1✔
285
            params: None,
1✔
286
            return_type: None,
1✔
287
            visibility: Visibility::All,
288
            line_number: node.start_position().row + 1,
2✔
289
            full_signature: full_sig,
1✔
290
        })
291
    }
292
    fn extract_class_signature(
1✔
293
        &self,
294
        source: &str,
295
        node: &tree_sitter::Node,
296
        visibility: Visibility,
297
    ) -> Option<Signature> {
298
        let name = self.find_child_text(node, "type_identifier", source)?;
2✔
299

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

305
        Some(Signature {
1✔
306
            kind: SignatureKind::Class,
307
            name,
1✔
308
            params: None,
1✔
309
            return_type: None,
1✔
310
            visibility,
311
            line_number: node.start_position().row + 1,
2✔
312
            full_signature: full_sig,
1✔
313
        })
314
    }
315

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

323
        // Byte-slice to preserve inheritance/templates (e.g. `struct Foo : public Base`).
324
        let full_sig = slice_signature_before_body(source, node, &["field_declaration_list"])
1✔
325
            .unwrap_or_else(|| format!("struct {}", name));
1✔
326

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

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

341
        // Byte-slice to preserve `enum class` and the underlying type (e.g. `: int`).
342
        let full_sig = slice_signature_before_body(source, node, &["enumerator_list"])
1✔
343
            .unwrap_or_else(|| format!("enum {}", name));
1✔
344

345
        Some(Signature {
1✔
346
            kind: SignatureKind::Enum,
347
            name,
1✔
348
            params: None,
1✔
349
            return_type: None,
1✔
350
            visibility: Visibility::All,
351
            line_number: node.start_position().row + 1,
2✔
352
            full_signature: full_sig,
1✔
353
        })
354
    }
355

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

359
        // Preserve the full alias target, e.g. `using StringVec = std::vector<std::string>`
360
        // or `typedef unsigned int uint`, instead of a bare `using/typedef X`.
361
        let text = source[node.start_byte()..node.end_byte()].trim_end();
2✔
362
        let full_sig = text.trim_end_matches(';').trim_end().to_string();
1✔
363

364
        Some(Signature {
1✔
365
            kind: SignatureKind::TypeAlias,
366
            name,
1✔
367
            params: None,
1✔
368
            return_type: None,
1✔
369
            visibility: Visibility::All,
370
            line_number: node.start_position().row + 1,
2✔
371
            full_signature: full_sig,
1✔
372
        })
373
    }
374

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

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

380
        Some(Signature {
1✔
381
            kind: SignatureKind::Macro,
382
            name,
1✔
383
            params: None,
1✔
384
            return_type: None,
1✔
385
            visibility: Visibility::All,
386
            line_number: node.start_position().row + 1,
2✔
387
            full_signature: full_sig,
1✔
388
        })
389
    }
390

391
    fn find_function_name(&self, node: &tree_sitter::Node, source: &str) -> Option<String> {
1✔
392
        // Resolve the name strictly from inside the `function_declarator`,
393
        // descending through pointer/reference/parenthesized wrappers so that
394
        // pointer- and reference-returning functions are not dropped. We do NOT
395
        // fall back to a sibling identifier — doing so would misread a qualified
396
        // return type like `std::string` as the function name.
397
        let decl = self.find_function_declarator(node)?;
1✔
398
        self.declarator_name(&decl, source)
1✔
399
    }
400

401
    fn find_function_declarator<'a>(
1✔
402
        &self,
403
        node: &tree_sitter::Node<'a>,
404
    ) -> Option<tree_sitter::Node<'a>> {
405
        let mut cursor = node.walk();
1✔
406
        for child in node.children(&mut cursor) {
2✔
407
            match child.kind() {
2✔
408
                "function_declarator" => return Some(child),
2✔
409
                "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => {
410
                    if let Some(found) = self.find_function_declarator(&child) {
2✔
411
                        return Some(found);
1✔
412
                    }
413
                }
NEW
414
                _ => {}
×
415
            }
416
        }
NEW
417
        None
×
418
    }
419

420
    /// Resolve the declared name inside a `function_declarator`, skipping the
421
    /// parameter list and descending through nested declarators.
422
    fn declarator_name(&self, node: &tree_sitter::Node, source: &str) -> Option<String> {
1✔
423
        let mut cursor = node.walk();
1✔
424
        for child in node.children(&mut cursor) {
2✔
425
            match child.kind() {
2✔
426
                "identifier"
1✔
427
                | "qualified_identifier"
428
                | "field_identifier"
429
                | "destructor_name"
430
                | "operator_name" => {
431
                    return Some(source[child.start_byte()..child.end_byte()].to_string());
2✔
432
                }
NEW
433
                "pointer_declarator"
×
434
                | "reference_declarator"
435
                | "parenthesized_declarator"
436
                | "function_declarator" => {
NEW
437
                    if let Some(name) = self.declarator_name(&child, source) {
×
NEW
438
                        return Some(name);
×
439
                    }
440
                }
441
                _ => {}
442
            }
443
        }
UNCOV
444
        None
×
445
    }
446

447
    fn find_return_type(&self, node: &tree_sitter::Node, source: &str) -> Option<String> {
1✔
448
        let mut cursor = node.walk();
1✔
449
        for child in node.children(&mut cursor) {
2✔
450
            match child.kind() {
2✔
451
                "primitive_type" | "type_identifier" | "sized_type_specifier" => {
2✔
452
                    return Some(source[child.start_byte()..child.end_byte()].to_string());
2✔
453
                }
454
                _ => {}
455
            }
456
        }
457
        None
1✔
458
    }
459

460
    fn find_child_text(
1✔
461
        &self,
462
        node: &tree_sitter::Node,
463
        kind: &str,
464
        source: &str,
465
    ) -> Option<String> {
466
        let mut cursor = node.walk();
1✔
467
        for child in node.children(&mut cursor) {
2✔
468
            if child.kind() == kind {
2✔
469
                return Some(source[child.start_byte()..child.end_byte()].to_string());
1✔
470
            }
471
        }
472
        None
1✔
473
    }
474

475
    fn find_best_boundary(
×
476
        &self,
477
        cursor: &mut tree_sitter::TreeCursor,
478
        max_bytes: usize,
479
        best_end: &mut usize,
480
    ) {
481
        loop {
482
            let node = cursor.node();
×
483
            let end_byte = node.end_byte();
×
484

485
            if end_byte <= max_bytes && end_byte > *best_end {
×
486
                let is_item = matches!(
×
487
                    node.kind(),
×
488
                    "function_definition"
489
                        | "class_specifier"
490
                        | "struct_specifier"
491
                        | "enum_specifier"
492
                        | "alias_declaration"
493
                        | "type_definition"
494
                );
495
                if is_item {
×
496
                    *best_end = end_byte;
×
497
                }
498
            }
499

500
            if cursor.goto_first_child() {
×
501
                self.find_best_boundary(cursor, max_bytes, best_end);
×
502
                cursor.goto_parent();
×
503
            }
504

505
            if !cursor.goto_next_sibling() {
×
506
                break;
507
            }
508
        }
509
    }
510
}
511

512
#[cfg(test)]
513
mod tests {
514
    use super::*;
515

516
    #[test]
517
    fn test_extract_class_signature() {
518
        let source = r#"
519
class HelloWorld {
520
public:
521
    void greet() {
522
        std::cout << "Hello" << std::endl;
523
    }
524
};
525
"#;
526

527
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
528
        let classes: Vec<_> = signatures
529
            .iter()
530
            .filter(|s| s.kind == SignatureKind::Class)
531
            .collect();
532
        assert!(!classes.is_empty());
533
        assert_eq!(classes[0].name, "HelloWorld");
534
    }
535

536
    #[test]
537
    fn test_extract_function_signature() {
538
        let source = r#"
539
int add(int a, int b) {
540
    return a + b;
541
}
542

543
void greet(const std::string& name) {
544
    std::cout << name << std::endl;
545
}
546
"#;
547

548
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
549
        let funcs: Vec<_> = signatures
550
            .iter()
551
            .filter(|s| s.kind == SignatureKind::Function)
552
            .collect();
553
        assert!(funcs.len() >= 2);
554
    }
555

556
    #[test]
557
    fn test_pointer_and_qualified_return_functions() {
558
        // Regression: pointer-returning functions were dropped, and a qualified
559
        // return type (`std::string`) was misread as the function name.
560
        let source = r#"
561
int* makeArray(int n) {
562
    return nullptr;
563
}
564

565
std::string greet(const std::string& who) {
566
    return who;
567
}
568
"#;
569

570
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
571
        let names: Vec<&str> = signatures
572
            .iter()
573
            .filter(|s| s.kind == SignatureKind::Function)
574
            .map(|s| s.name.as_str())
575
            .collect();
576
        assert!(
577
            names.contains(&"makeArray"),
578
            "pointer-returning function dropped; got {:?}",
579
            names
580
        );
581
        assert!(
582
            names.contains(&"greet"),
583
            "function with qualified return type dropped; got {:?}",
584
            names
585
        );
586
        assert!(
587
            !names.contains(&"std::string"),
588
            "qualified return type misread as function name; got {:?}",
589
            names
590
        );
591
    }
592

593
    #[test]
594
    fn test_extract_struct_signature() {
595
        let source = r#"
596
struct Vec3 {
597
    float x, y, z;
598
};
599
"#;
600

601
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
602
        let structs: Vec<_> = signatures
603
            .iter()
604
            .filter(|s| s.kind == SignatureKind::Struct)
605
            .collect();
606
        assert!(!structs.is_empty());
607
        assert_eq!(structs[0].name, "Vec3");
608
    }
609

610
    #[test]
611
    fn test_extract_enum_signature() {
612
        let source = r#"
613
enum class Direction {
614
    Up,
615
    Down,
616
    Left,
617
    Right
618
};
619
"#;
620

621
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
622
        let enums: Vec<_> = signatures
623
            .iter()
624
            .filter(|s| s.kind == SignatureKind::Enum)
625
            .collect();
626
        assert!(!enums.is_empty());
627
        assert_eq!(enums[0].name, "Direction");
628
    }
629

630
    #[test]
631
    fn test_struct_enum_alias_preserve_details() {
632
        // Regression (B17): struct inheritance, enum underlying type, and alias
633
        // targets were dropped by `format!`-based signatures.
634
        let source = r#"
635
struct Derived : public Base {
636
    int x;
637
};
638

639
enum class Color : int {
640
    Red,
641
    Green
642
};
643

644
using StringVec = std::vector<std::string>;
645
"#;
646

647
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
648
        let s = signatures
649
            .iter()
650
            .find(|s| s.kind == SignatureKind::Struct && s.name == "Derived")
651
            .expect("struct extracted");
652
        assert!(
653
            s.full_signature.contains(": public Base"),
654
            "struct inheritance dropped: {}",
655
            s.full_signature
656
        );
657
        let e = signatures
658
            .iter()
659
            .find(|s| s.kind == SignatureKind::Enum)
660
            .expect("enum extracted");
661
        assert!(
662
            e.full_signature.contains(": int"),
663
            "enum underlying type dropped: {}",
664
            e.full_signature
665
        );
666
        let a = signatures
667
            .iter()
668
            .find(|s| s.kind == SignatureKind::TypeAlias)
669
            .expect("alias extracted");
670
        assert!(
671
            a.full_signature.contains("std::vector"),
672
            "alias target dropped: {}",
673
            a.full_signature
674
        );
675
    }
676

677
    #[test]
678
    fn test_extract_header_prototype() {
679
        let source = r#"
680
int add(int a, int b);
681
void greet(const std::string& name);
682
"#;
683

684
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
685
        let funcs: Vec<_> = signatures
686
            .iter()
687
            .filter(|s| s.kind == SignatureKind::Function)
688
            .collect();
689
        assert!(funcs.len() >= 2);
690
        for f in &funcs {
691
            assert!(!f.full_signature.ends_with(';'));
692
        }
693
    }
694

695
    #[test]
696
    fn test_extract_template_class_with_inheritance() {
697
        let source = r#"
698
template<typename T>
699
class Container : public Base {
700
    T value;
701
public:
702
    T get() { return value; }
703
};
704
"#;
705

706
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
707
        let classes: Vec<_> = signatures
708
            .iter()
709
            .filter(|s| s.kind == SignatureKind::Class)
710
            .collect();
711
        assert!(!classes.is_empty());
712
        // Byte-slicing fix should preserve template<> and : public Base
713
        let sig = &classes[0].full_signature;
714
        assert!(sig.contains("Container"));
715
    }
716

717
    #[test]
718
    fn test_extract_type_alias() {
719
        let source = r#"
720
using StringVec = std::vector<std::string>;
721
typedef unsigned int uint;
722
"#;
723

724
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
725
        let aliases: Vec<_> = signatures
726
            .iter()
727
            .filter(|s| s.kind == SignatureKind::TypeAlias)
728
            .collect();
729
        assert!(!aliases.is_empty());
730
    }
731

732
    #[test]
733
    fn test_extract_macro() {
734
        let source = r#"
735
#define MIN(a, b) ((a) < (b) ? (a) : (b))
736
"#;
737

738
        let signatures = CppSupport.extract_signatures(source, Visibility::All);
739
        let macros: Vec<_> = signatures
740
            .iter()
741
            .filter(|s| s.kind == SignatureKind::Macro)
742
            .collect();
743
        assert!(!macros.is_empty());
744
        assert_eq!(macros[0].name, "MIN");
745
    }
746

747
    #[test]
748
    fn test_extract_structure() {
749
        let source = r#"
750
#include <iostream>
751
#include <vector>
752

753
class Foo {
754
public:
755
    void bar() {}
756
};
757

758
struct Point { int x; int y; };
759
enum Color { R, G, B };
760

761
void helper() {}
762
"#;
763

764
        let structure = CppSupport.extract_structure(source);
765
        assert!(structure.functions >= 1);
766
        assert!(structure.classes >= 1);
767
        assert!(structure.structs >= 1);
768
        assert!(structure.enums >= 1);
769
        assert!(structure.imports.len() >= 2);
770
    }
771

772
    #[test]
773
    fn test_parse_valid_cpp() {
774
        let source = "int main() { return 0; }";
775
        let tree = CppSupport.parse(source);
776
        assert!(tree.is_some());
777
    }
778

779
    #[test]
780
    fn test_find_truncation_point() {
781
        let source = "int main() { return 0; }";
782
        let point = CppSupport.find_truncation_point(source, 1000);
783
        assert_eq!(point, source.len());
784
    }
785

786
    #[test]
787
    fn test_file_extensions() {
788
        assert!(CppSupport.supports_extension("cpp"));
789
        assert!(CppSupport.supports_extension("hpp"));
790
        assert!(CppSupport.supports_extension("cxx"));
791
        assert!(!CppSupport.supports_extension("c"));
792
    }
793
}
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