• 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

78.4
/src/tree_sitter/languages/java.rs
1
//! Java language support for tree-sitter.
2

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

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

12
pub struct JavaSupport;
13

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

21
#[cfg(feature = "tree-sitter-java")]
22
impl LanguageSupport for JavaSupport {
23
    fn file_extensions(&self) -> &[&'static str] {
1✔
24
        &["java"]
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-java")]
86
impl JavaSupport {
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
            "method_declaration" => {
1✔
96
                if let Some(sig) = self.extract_method_signature(source, node, visibility) {
2✔
97
                    signatures.push(sig);
1✔
98
                }
99
            }
100
            "class_declaration" => {
1✔
101
                if let Some(sig) = self.extract_class_signature(source, node, visibility) {
2✔
102
                    signatures.push(sig);
1✔
103
                }
104
            }
105
            "interface_declaration" => {
1✔
106
                if let Some(sig) = self.extract_interface_signature(source, node, visibility) {
2✔
107
                    signatures.push(sig);
1✔
108
                }
109
            }
110
            "enum_declaration" => {
1✔
111
                if let Some(sig) = self.extract_enum_signature(source, node, visibility) {
2✔
112
                    signatures.push(sig);
1✔
113
                }
114
            }
115
            "field_declaration" => {
1✔
116
                if let Some(sig) = self.extract_field_signature(source, node, visibility) {
×
117
                    signatures.push(sig);
×
118
                }
119
            }
120
            _ => {}
121
        }
122

123
        let mut cursor = node.walk();
1✔
124
        for child in node.children(&mut cursor) {
2✔
125
            self.extract_signatures_from_node(source, &child, visibility, signatures);
2✔
126
        }
127
    }
128

129
    fn extract_structure_from_node(&self, node: &tree_sitter::Node, structure: &mut CodeStructure) {
1✔
130
        match node.kind() {
1✔
131
            "method_declaration" => structure.functions += 1,
3✔
132
            "class_declaration" => structure.classes += 1,
3✔
133
            "interface_declaration" => structure.interfaces += 1,
3✔
134
            "enum_declaration" => structure.enums += 1,
3✔
135
            "import_declaration" => {
1✔
136
                structure.imports.push("import".to_string());
1✔
137
            }
138
            _ => {}
139
        }
140

141
        let mut cursor = node.walk();
1✔
142
        for child in node.children(&mut cursor) {
2✔
143
            self.extract_structure_from_node(&child, structure);
2✔
144
        }
145
    }
146

147
    /// Text of the declaration's `modifiers` child, if present.
148
    fn modifiers_text<'a>(&self, node: &tree_sitter::Node, source: &'a str) -> Option<&'a str> {
1✔
149
        let mut cursor = node.walk();
1✔
150
        for child in node.children(&mut cursor) {
2✔
151
            if child.kind() == "modifiers" {
2✔
152
                return Some(&source[child.start_byte()..child.end_byte()]);
1✔
153
            }
154
        }
155
        None
1✔
156
    }
157

158
    /// True when the declaration's nearest enclosing type is an interface or
159
    /// annotation type — whose members are implicitly `public`.
160
    fn is_interface_member(&self, node: &tree_sitter::Node) -> bool {
1✔
161
        let mut current = node.parent();
1✔
162
        while let Some(n) = current {
2✔
163
            match n.kind() {
1✔
164
                "interface_declaration" | "annotation_type_declaration" => return true,
2✔
165
                // The nearest enclosing type is a class/enum/record, whose
166
                // members are package-private by default — stop here.
167
                "class_declaration" | "enum_declaration" | "record_declaration" => return false,
1✔
168
                _ => {}
169
            }
170
            current = n.parent();
1✔
171
        }
NEW
172
        false
×
173
    }
174

175
    /// Determine a Java declaration's *effective* visibility, honoring Java's
176
    /// implicit rules. An explicit `public` always wins, and an explicit
177
    /// `private`/`protected` is respected even inside an interface. Otherwise a
178
    /// declaration with no access modifier is **package-private** in a class/enum
179
    /// but **implicitly public** as an interface/annotation member — without that
180
    /// distinction, a public interface's methods (which carry no `modifiers`
181
    /// node) would be classified non-public and dropped under `--visibility public`.
182
    fn get_visibility(&self, node: &tree_sitter::Node, source: &str) -> Visibility {
1✔
183
        if let Some(text) = self.modifiers_text(node, source) {
1✔
184
            if text.split_whitespace().any(|t| t == "public") {
3✔
185
                return Visibility::Public;
1✔
186
            }
187
            if text
1✔
188
                .split_whitespace()
189
                .any(|t| t == "private" || t == "protected")
3✔
190
            {
191
                return Visibility::Private;
1✔
192
            }
193
            // Only non-access modifiers (e.g. `default`, `static`, `final`,
194
            // `abstract`) — fall through to the implicit rule below.
195
        }
196
        if self.is_interface_member(node) {
3✔
197
            Visibility::Public
1✔
198
        } else {
199
            Visibility::Private
1✔
200
        }
201
    }
202

203
    fn extract_method_signature(
1✔
204
        &self,
205
        source: &str,
206
        node: &tree_sitter::Node,
207
        visibility: Visibility,
208
    ) -> Option<Signature> {
209
        let vis = self.get_visibility(node, source);
1✔
210

211
        if visibility == Visibility::Public && vis != Visibility::Public {
2✔
212
            return None;
1✔
213
        }
214
        if visibility == Visibility::Private && vis == Visibility::Public {
2✔
215
            return None;
1✔
216
        }
217

218
        let name = self.find_child_text(node, "identifier", source)?;
2✔
219
        let params = self.find_child_text(node, "formal_parameters", source);
2✔
220
        let return_type = self
221
            .find_child_text(node, "type_identifier", source)
1✔
222
            .or_else(|| self.find_child_text_for_type(node, source));
3✔
223

224
        // Use byte-slicing to preserve annotations, generics, throws, and modifiers
225
        let full_sig = slice_signature_before_body(source, node, &["block"]).unwrap_or_else(|| {
3✔
226
            let mut sig = String::new();
1✔
227
            if vis == Visibility::Public {
2✔
228
                sig.push_str("public ");
1✔
229
            }
230
            if let Some(r) = &return_type {
2✔
231
                sig.push_str(r);
2✔
232
                sig.push(' ');
1✔
233
            }
234
            sig.push_str(&name);
2✔
235
            if let Some(p) = &params {
1✔
236
                sig.push_str(p);
2✔
237
            } else {
238
                sig.push_str("()");
×
239
            }
240
            sig
1✔
241
        });
242

243
        Some(Signature {
1✔
244
            kind: SignatureKind::Method,
245
            name,
1✔
246
            params,
1✔
247
            return_type,
1✔
248
            visibility: vis,
249
            line_number: node.start_position().row + 1,
2✔
250
            full_signature: full_sig,
1✔
251
        })
252
    }
253

254
    fn extract_class_signature(
1✔
255
        &self,
256
        source: &str,
257
        node: &tree_sitter::Node,
258
        visibility: Visibility,
259
    ) -> Option<Signature> {
260
        let vis = self.get_visibility(node, source);
1✔
261

262
        if visibility == Visibility::Public && vis != Visibility::Public {
2✔
263
            return None;
×
264
        }
265
        if visibility == Visibility::Private && vis == Visibility::Public {
2✔
266
            return None;
1✔
267
        }
268

269
        let name = self.find_child_text(node, "identifier", source)?;
2✔
270

271
        let mut full_sig = String::new();
1✔
272
        if vis == Visibility::Public {
2✔
273
            full_sig.push_str("public ");
1✔
274
        }
275
        full_sig.push_str("class ");
1✔
276
        full_sig.push_str(&name);
1✔
277

278
        Some(Signature {
1✔
279
            kind: SignatureKind::Class,
280
            name,
1✔
281
            params: None,
1✔
282
            return_type: None,
1✔
283
            visibility: vis,
284
            line_number: node.start_position().row + 1,
2✔
285
            full_signature: full_sig,
1✔
286
        })
287
    }
288

289
    fn extract_interface_signature(
1✔
290
        &self,
291
        source: &str,
292
        node: &tree_sitter::Node,
293
        visibility: Visibility,
294
    ) -> Option<Signature> {
295
        let vis = self.get_visibility(node, source);
1✔
296

297
        if visibility == Visibility::Public && vis != Visibility::Public {
2✔
298
            return None;
×
299
        }
300
        if visibility == Visibility::Private && vis == Visibility::Public {
1✔
301
            return None;
×
302
        }
303

304
        let name = self.find_child_text(node, "identifier", source)?;
2✔
305

306
        let mut full_sig = String::new();
1✔
307
        if vis == Visibility::Public {
2✔
308
            full_sig.push_str("public ");
1✔
309
        }
310
        full_sig.push_str("interface ");
1✔
311
        full_sig.push_str(&name);
1✔
312

313
        Some(Signature {
1✔
314
            kind: SignatureKind::Interface,
315
            name,
1✔
316
            params: None,
1✔
317
            return_type: None,
1✔
318
            visibility: vis,
319
            line_number: node.start_position().row + 1,
2✔
320
            full_signature: full_sig,
1✔
321
        })
322
    }
323

324
    fn extract_enum_signature(
1✔
325
        &self,
326
        source: &str,
327
        node: &tree_sitter::Node,
328
        visibility: Visibility,
329
    ) -> Option<Signature> {
330
        let vis = self.get_visibility(node, source);
1✔
331

332
        if visibility == Visibility::Public && vis != Visibility::Public {
1✔
333
            return None;
×
334
        }
335
        if visibility == Visibility::Private && vis == Visibility::Public {
1✔
336
            return None;
×
337
        }
338

339
        let name = self.find_child_text(node, "identifier", source)?;
2✔
340

341
        let mut full_sig = String::new();
1✔
342
        if vis == Visibility::Public {
2✔
343
            full_sig.push_str("public ");
1✔
344
        }
345
        full_sig.push_str("enum ");
1✔
346
        full_sig.push_str(&name);
1✔
347

348
        Some(Signature {
1✔
349
            kind: SignatureKind::Enum,
350
            name,
1✔
351
            params: None,
1✔
352
            return_type: None,
1✔
353
            visibility: vis,
354
            line_number: node.start_position().row + 1,
2✔
355
            full_signature: full_sig,
1✔
356
        })
357
    }
358

359
    fn extract_field_signature(
×
360
        &self,
361
        source: &str,
362
        node: &tree_sitter::Node,
363
        visibility: Visibility,
364
    ) -> Option<Signature> {
NEW
365
        let vis = self.get_visibility(node, source);
×
366

367
        if visibility == Visibility::Public && vis != Visibility::Public {
×
368
            return None;
×
369
        }
370
        if visibility == Visibility::Private && vis == Visibility::Public {
×
371
            return None;
×
372
        }
373

374
        let name = self.find_child_text(node, "identifier", source)?;
×
375
        let full_signature = format!("field {}", &name);
×
376

377
        Some(Signature {
×
378
            kind: SignatureKind::Constant,
379
            name,
×
380
            params: None,
×
381
            return_type: None,
×
382
            visibility: vis,
383
            line_number: node.start_position().row + 1,
×
384
            full_signature,
×
385
        })
386
    }
387

388
    fn find_child_text(
1✔
389
        &self,
390
        node: &tree_sitter::Node,
391
        kind: &str,
392
        source: &str,
393
    ) -> Option<String> {
394
        let mut cursor = node.walk();
1✔
395
        for child in node.children(&mut cursor) {
2✔
396
            if child.kind() == kind {
2✔
397
                return Some(source[child.start_byte()..child.end_byte()].to_string());
1✔
398
            }
399
        }
400
        None
1✔
401
    }
402

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

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

426
            if end_byte <= max_bytes && end_byte > *best_end {
×
427
                let is_item = matches!(
×
428
                    node.kind(),
×
429
                    "method_declaration"
430
                        | "class_declaration"
431
                        | "interface_declaration"
432
                        | "enum_declaration"
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
public class HelloWorld {
459
    public static void main(String[] args) {
460
        System.out.println("Hello");
461
    }
462
}
463
}
464
"#;
465

466
        let signatures = JavaSupport.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_method_signature() {
477
        let source = r#"
478
public class Calculator {
479
    public int add(int a, int b) {
480
        return a + b;
481
    }
482

483
    private double multiply(double x, double y) {
484
        return x * y;
485
    }
486
}
487
"#;
488

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

497
    #[test]
498
    fn test_extract_interface_signature() {
499
        let source = r#"
500
public interface Printable {
501
    void print();
502
    String format(String template);
503
}
504
"#;
505

506
        let signatures = JavaSupport.extract_signatures(source, Visibility::All);
507
        let interfaces: Vec<_> = signatures
508
            .iter()
509
            .filter(|s| s.kind == SignatureKind::Interface)
510
            .collect();
511
        assert!(!interfaces.is_empty());
512
        assert_eq!(interfaces[0].name, "Printable");
513
    }
514

515
    #[test]
516
    fn test_extract_enum_signature() {
517
        let source = r#"
518
public enum Color {
519
    RED, GREEN, BLUE;
520
}
521
"#;
522

523
        let signatures = JavaSupport.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, "Color");
530
    }
531

532
    #[test]
533
    fn test_extract_class_with_inheritance() {
534
        let source = r#"
535
public class Dog extends Animal implements Runnable {
536
    public void run() {}
537
}
538
"#;
539

540
        let signatures = JavaSupport.extract_signatures(source, Visibility::All);
541
        let classes: Vec<_> = signatures
542
            .iter()
543
            .filter(|s| s.kind == SignatureKind::Class)
544
            .collect();
545
        assert!(!classes.is_empty());
546
        assert_eq!(classes[0].name, "Dog");
547
    }
548

549
    #[test]
550
    fn test_visibility_public_filter() {
551
        // Regression: get_visibility returned All unconditionally, so the
552
        // `public` filter dropped every symbol.
553
        let source = r#"
554
public class Api {
555
    public int publicMethod() { return 1; }
556
    private int privateMethod() { return 2; }
557
    int packageMethod() { return 3; }
558
}
559
"#;
560

561
        let public_only = JavaSupport.extract_signatures(source, Visibility::Public);
562
        assert!(public_only.iter().any(|s| s.name == "publicMethod"));
563
        assert!(
564
            !public_only.iter().any(|s| s.name == "privateMethod"),
565
            "private method leaked into `public` filter"
566
        );
567
        assert!(
568
            !public_only.iter().any(|s| s.name == "packageMethod"),
569
            "package-private method leaked into `public` filter"
570
        );
571
    }
572

573
    #[test]
574
    fn test_visibility_private_filter_excludes_public() {
575
        let source = r#"
576
public class Api {
577
    public int publicMethod() { return 1; }
578
    private int privateMethod() { return 2; }
579
}
580
"#;
581

582
        let private_only = JavaSupport.extract_signatures(source, Visibility::Private);
583
        assert!(private_only.iter().any(|s| s.name == "privateMethod"));
584
        assert!(
585
            !private_only.iter().any(|s| s.name == "publicMethod"),
586
            "public method leaked into `private` filter"
587
        );
588
        assert!(
589
            !private_only.iter().any(|s| s.name == "Api"),
590
            "public class leaked into `private` filter"
591
        );
592
    }
593

594
    #[test]
595
    fn test_interface_methods_survive_public_filter() {
596
        // Regression: interface methods are implicitly public but carry no
597
        // `modifiers` node, so they were classified package-private and dropped
598
        // under `--visibility public`, hiding a public interface's API. An
599
        // explicitly `private` interface method (Java 9+) must still be filtered.
600
        let source = r#"
601
public interface Service {
602
    void start();
603
    String name();
604
    private void internalHelper() {}
605
}
606
"#;
607

608
        let public_only = JavaSupport.extract_signatures(source, Visibility::Public);
609
        assert!(
610
            public_only.iter().any(|s| s.name == "start"),
611
            "implicitly-public interface method must pass the `public` filter"
612
        );
613
        assert!(
614
            public_only.iter().any(|s| s.name == "name"),
615
            "implicitly-public interface method must pass the `public` filter"
616
        );
617
        assert!(
618
            !public_only.iter().any(|s| s.name == "internalHelper"),
619
            "explicitly-private interface method must NOT pass the `public` filter"
620
        );
621
    }
622

623
    #[test]
624
    fn test_extract_structure() {
625
        let source = r#"
626
import java.util.List;
627
import java.util.Map;
628

629
public class App {
630
    public void doStuff() {}
631
    private void helper() {}
632
}
633

634
interface Printable {
635
    void print();
636
}
637

638
enum Status { ACTIVE, INACTIVE }
639
"#;
640

641
        let structure = JavaSupport.extract_structure(source);
642
        assert!(structure.functions >= 2);
643
        assert!(structure.classes >= 1);
644
        assert!(structure.interfaces >= 1);
645
        assert!(structure.enums >= 1);
646
        assert!(structure.imports.len() >= 2);
647
    }
648

649
    #[test]
650
    fn test_parse_valid_java() {
651
        let source = "public class Main { public static void main(String[] args) {} }";
652
        let tree = JavaSupport.parse(source);
653
        assert!(tree.is_some());
654
    }
655

656
    #[test]
657
    fn test_find_truncation_point() {
658
        let source = "public class Main { public static void main(String[] args) {} }";
659
        let point = JavaSupport.find_truncation_point(source, 1000);
660
        assert_eq!(point, source.len());
661
    }
662

663
    #[test]
664
    fn test_file_extensions() {
665
        assert!(JavaSupport.supports_extension("java"));
666
        assert!(!JavaSupport.supports_extension("rs"));
667
    }
668
}
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