Skip to main content

codelore_rca/metrics/
loc.rs

1use std::collections::HashSet;
2
3use crate::checker::Checker;
4use serde::Serialize;
5use serde::ser::{SerializeStruct, Serializer};
6use std::fmt;
7
8use crate::macros::implement_metric_trait;
9use crate::*;
10
11/// The `SLoc` metric suite.
12#[derive(Debug, Clone)]
13pub struct Sloc {
14    start: usize,
15    end: usize,
16    unit: bool,
17    sloc_min: usize,
18    sloc_max: usize,
19}
20
21impl Default for Sloc {
22    fn default() -> Self {
23        Self {
24            start: 0,
25            end: 0,
26            unit: false,
27            sloc_min: usize::MAX,
28            sloc_max: 0,
29        }
30    }
31}
32
33impl Sloc {
34    #[inline(always)]
35    pub fn sloc(&self) -> f64 {
36        // This metric counts the number of lines in a file
37        // The if construct is needed to count the line of code that represents
38        // the function signature in a function space
39        let sloc = if self.unit {
40            self.end - self.start
41        } else {
42            (self.end - self.start) + 1
43        };
44        sloc as f64
45    }
46
47    /// The `Sloc` metric minimum value.
48    #[inline(always)]
49    pub fn sloc_min(&self) -> f64 {
50        self.sloc_min as f64
51    }
52
53    /// The `Sloc` metric maximum value.
54    #[inline(always)]
55    pub fn sloc_max(&self) -> f64 {
56        self.sloc_max as f64
57    }
58
59    #[inline(always)]
60    pub fn merge(&mut self, other: &Sloc) {
61        self.sloc_min = self.sloc_min.min(other.sloc() as usize);
62        self.sloc_max = self.sloc_max.max(other.sloc() as usize);
63    }
64
65    #[inline(always)]
66    pub(crate) fn compute_minmax(&mut self) {
67        if self.sloc_min == usize::MAX {
68            self.sloc_min = self.sloc_min.min(self.sloc() as usize);
69            self.sloc_max = self.sloc_max.max(self.sloc() as usize);
70        }
71    }
72}
73
74/// The `PLoc` metric suite.
75#[derive(Debug, Clone)]
76pub struct Ploc {
77    lines: HashSet<usize>,
78    ploc_min: usize,
79    ploc_max: usize,
80}
81
82impl Default for Ploc {
83    fn default() -> Self {
84        Self {
85            lines: HashSet::default(),
86            ploc_min: usize::MAX,
87            ploc_max: 0,
88        }
89    }
90}
91
92impl Ploc {
93    #[inline(always)]
94    pub fn ploc(&self) -> f64 {
95        // This metric counts the number of instruction lines in a code
96        // https://en.wikipedia.org/wiki/Source_lines_of_code
97        self.lines.len() as f64
98    }
99
100    /// The `Ploc` metric minimum value.
101    #[inline(always)]
102    pub fn ploc_min(&self) -> f64 {
103        self.ploc_min as f64
104    }
105
106    /// The `Ploc` metric maximum value.
107    #[inline(always)]
108    pub fn ploc_max(&self) -> f64 {
109        self.ploc_max as f64
110    }
111
112    #[inline(always)]
113    pub fn merge(&mut self, other: &Ploc) {
114        // Merge ploc lines
115        for l in other.lines.iter() {
116            self.lines.insert(*l);
117        }
118
119        self.ploc_min = self.ploc_min.min(other.ploc() as usize);
120        self.ploc_max = self.ploc_max.max(other.ploc() as usize);
121    }
122
123    #[inline(always)]
124    pub(crate) fn compute_minmax(&mut self) {
125        if self.ploc_min == usize::MAX {
126            self.ploc_min = self.ploc_min.min(self.ploc() as usize);
127            self.ploc_max = self.ploc_max.max(self.ploc() as usize);
128        }
129    }
130}
131
132/// The `CLoc` metric suite.
133#[derive(Debug, Clone)]
134pub struct Cloc {
135    only_comment_lines: usize,
136    code_comment_lines: usize,
137    comment_line_end: Option<usize>,
138    cloc_min: usize,
139    cloc_max: usize,
140}
141
142impl Default for Cloc {
143    fn default() -> Self {
144        Self {
145            only_comment_lines: 0,
146            code_comment_lines: 0,
147            comment_line_end: Option::default(),
148            cloc_min: usize::MAX,
149            cloc_max: 0,
150        }
151    }
152}
153
154impl Cloc {
155    #[inline(always)]
156    pub fn cloc(&self) -> f64 {
157        // Comments are counted regardless of their placement
158        // https://en.wikipedia.org/wiki/Source_lines_of_code
159        (self.only_comment_lines + self.code_comment_lines) as f64
160    }
161
162    /// The `Ploc` metric minimum value.
163    #[inline(always)]
164    pub fn cloc_min(&self) -> f64 {
165        self.cloc_min as f64
166    }
167
168    /// The `Ploc` metric maximum value.
169    #[inline(always)]
170    pub fn cloc_max(&self) -> f64 {
171        self.cloc_max as f64
172    }
173
174    #[inline(always)]
175    pub fn merge(&mut self, other: &Cloc) {
176        // Merge cloc lines
177        self.only_comment_lines += other.only_comment_lines;
178        self.code_comment_lines += other.code_comment_lines;
179
180        self.cloc_min = self.cloc_min.min(other.cloc() as usize);
181        self.cloc_max = self.cloc_max.max(other.cloc() as usize);
182    }
183
184    #[inline(always)]
185    pub(crate) fn compute_minmax(&mut self) {
186        if self.cloc_min == usize::MAX {
187            self.cloc_min = self.cloc_min.min(self.cloc() as usize);
188            self.cloc_max = self.cloc_max.max(self.cloc() as usize);
189        }
190    }
191}
192
193/// The `LLoc` metric suite.
194#[derive(Debug, Clone)]
195pub struct Lloc {
196    logical_lines: usize,
197    lloc_min: usize,
198    lloc_max: usize,
199}
200
201impl Default for Lloc {
202    fn default() -> Self {
203        Self {
204            logical_lines: 0,
205            lloc_min: usize::MAX,
206            lloc_max: 0,
207        }
208    }
209}
210
211impl Lloc {
212    #[inline(always)]
213    pub fn lloc(&self) -> f64 {
214        // This metric counts the number of statements in a code
215        // https://en.wikipedia.org/wiki/Source_lines_of_code
216        self.logical_lines as f64
217    }
218
219    /// The `Lloc` metric minimum value.
220    #[inline(always)]
221    pub fn lloc_min(&self) -> f64 {
222        self.lloc_min as f64
223    }
224
225    /// The `Lloc` metric maximum value.
226    #[inline(always)]
227    pub fn lloc_max(&self) -> f64 {
228        self.lloc_max as f64
229    }
230
231    #[inline(always)]
232    pub fn merge(&mut self, other: &Lloc) {
233        // Merge lloc lines
234        self.logical_lines += other.logical_lines;
235        self.lloc_min = self.lloc_min.min(other.lloc() as usize);
236        self.lloc_max = self.lloc_max.max(other.lloc() as usize);
237    }
238
239    #[inline(always)]
240    pub(crate) fn compute_minmax(&mut self) {
241        if self.lloc_min == usize::MAX {
242            self.lloc_min = self.lloc_min.min(self.lloc() as usize);
243            self.lloc_max = self.lloc_max.max(self.lloc() as usize);
244        }
245    }
246}
247
248/// The `Loc` metric suite.
249#[derive(Debug, Clone)]
250pub struct Stats {
251    sloc: Sloc,
252    ploc: Ploc,
253    cloc: Cloc,
254    lloc: Lloc,
255    space_count: usize,
256    blank_min: usize,
257    blank_max: usize,
258}
259
260impl Default for Stats {
261    fn default() -> Self {
262        Self {
263            sloc: Sloc::default(),
264            ploc: Ploc::default(),
265            cloc: Cloc::default(),
266            lloc: Lloc::default(),
267            space_count: 1,
268            blank_min: usize::MAX,
269            blank_max: 0,
270        }
271    }
272}
273
274impl Serialize for Stats {
275    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276    where
277        S: Serializer,
278    {
279        let mut st = serializer.serialize_struct("loc", 20)?;
280        st.serialize_field("sloc", &self.sloc())?;
281        st.serialize_field("ploc", &self.ploc())?;
282        st.serialize_field("lloc", &self.lloc())?;
283        st.serialize_field("cloc", &self.cloc())?;
284        st.serialize_field("blank", &self.blank())?;
285        st.serialize_field("sloc_average", &self.sloc_average())?;
286        st.serialize_field("ploc_average", &self.ploc_average())?;
287        st.serialize_field("lloc_average", &self.lloc_average())?;
288        st.serialize_field("cloc_average", &self.cloc_average())?;
289        st.serialize_field("blank_average", &self.blank_average())?;
290        st.serialize_field("sloc_min", &self.sloc_min())?;
291        st.serialize_field("sloc_max", &self.sloc_max())?;
292        st.serialize_field("cloc_min", &self.cloc_min())?;
293        st.serialize_field("cloc_max", &self.cloc_max())?;
294        st.serialize_field("ploc_min", &self.ploc_min())?;
295        st.serialize_field("ploc_max", &self.ploc_max())?;
296        st.serialize_field("lloc_min", &self.lloc_min())?;
297        st.serialize_field("lloc_max", &self.lloc_max())?;
298        st.serialize_field("blank_min", &self.blank_min())?;
299        st.serialize_field("blank_max", &self.blank_max())?;
300        st.end()
301    }
302}
303
304impl fmt::Display for Stats {
305    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306        write!(
307            f,
308            "sloc: {}, ploc: {}, lloc: {}, cloc: {}, blank: {}, sloc_average: {}, ploc_average: {}, lloc_average: {}, cloc_average: {}, blank_average: {}, sloc_min: {}, sloc_max: {}, cloc_min: {}, cloc_max: {}, ploc_min: {}, ploc_max: {}, lloc_min: {}, lloc_max: {}, blank_min: {}, blank_max: {}",
309            self.sloc(),
310            self.ploc(),
311            self.lloc(),
312            self.cloc(),
313            self.blank(),
314            self.sloc_average(),
315            self.ploc_average(),
316            self.lloc_average(),
317            self.cloc_average(),
318            self.blank_average(),
319            self.sloc_min(),
320            self.sloc_max(),
321            self.cloc_min(),
322            self.cloc_max(),
323            self.ploc_min(),
324            self.ploc_max(),
325            self.lloc_min(),
326            self.lloc_max(),
327            self.blank_min(),
328            self.blank_max(),
329        )
330    }
331}
332
333impl Stats {
334    /// Merges a second `Loc` metric suite into the first one
335    pub fn merge(&mut self, other: &Stats) {
336        self.sloc.merge(&other.sloc);
337        self.ploc.merge(&other.ploc);
338        self.cloc.merge(&other.cloc);
339        self.lloc.merge(&other.lloc);
340
341        // Count spaces
342        self.space_count += other.space_count;
343
344        // min and max
345
346        self.blank_min = self.blank_min.min(other.blank() as usize);
347        self.blank_max = self.blank_max.max(other.blank() as usize);
348    }
349
350    /// The `Sloc` metric.
351    ///
352    /// Counts the number of lines in a scope
353    #[inline(always)]
354    pub fn sloc(&self) -> f64 {
355        self.sloc.sloc()
356    }
357
358    /// The `Ploc` metric.
359    ///
360    /// Counts the number of instruction lines in a scope
361    #[inline(always)]
362    pub fn ploc(&self) -> f64 {
363        self.ploc.ploc()
364    }
365
366    /// The `Lloc` metric.
367    ///
368    /// Counts the number of statements in a scope
369    #[inline(always)]
370    pub fn lloc(&self) -> f64 {
371        self.lloc.lloc()
372    }
373
374    /// The `Cloc` metric.
375    ///
376    /// Counts the number of comments in a scope
377    #[inline(always)]
378    pub fn cloc(&self) -> f64 {
379        self.cloc.cloc()
380    }
381
382    /// The `Blank` metric.
383    ///
384    /// Counts the number of blank lines in a scope
385    #[inline(always)]
386    pub fn blank(&self) -> f64 {
387        self.sloc() - self.ploc() - self.cloc.only_comment_lines as f64
388    }
389
390    /// The `Sloc` metric average value.
391    ///
392    /// This value is computed dividing the `Sloc` value for the number of spaces
393    #[inline(always)]
394    pub fn sloc_average(&self) -> f64 {
395        self.sloc() / self.space_count as f64
396    }
397
398    /// The `Ploc` metric average value.
399    ///
400    /// This value is computed dividing the `Ploc` value for the number of spaces
401    #[inline(always)]
402    pub fn ploc_average(&self) -> f64 {
403        self.ploc() / self.space_count as f64
404    }
405
406    /// The `Lloc` metric average value.
407    ///
408    /// This value is computed dividing the `Lloc` value for the number of spaces
409    #[inline(always)]
410    pub fn lloc_average(&self) -> f64 {
411        self.lloc() / self.space_count as f64
412    }
413
414    /// The `Cloc` metric average value.
415    ///
416    /// This value is computed dividing the `Cloc` value for the number of spaces
417    #[inline(always)]
418    pub fn cloc_average(&self) -> f64 {
419        self.cloc() / self.space_count as f64
420    }
421
422    /// The `Blank` metric average value.
423    ///
424    /// This value is computed dividing the `Blank` value for the number of spaces
425    #[inline(always)]
426    pub fn blank_average(&self) -> f64 {
427        self.blank() / self.space_count as f64
428    }
429
430    /// The `Sloc` metric minimum value.
431    #[inline(always)]
432    pub fn sloc_min(&self) -> f64 {
433        self.sloc.sloc_min()
434    }
435
436    /// The `Sloc` metric maximum value.
437    #[inline(always)]
438    pub fn sloc_max(&self) -> f64 {
439        self.sloc.sloc_max()
440    }
441
442    /// The `Cloc` metric minimum value.
443    #[inline(always)]
444    pub fn cloc_min(&self) -> f64 {
445        self.cloc.cloc_min()
446    }
447
448    /// The `Cloc` metric maximum value.
449    #[inline(always)]
450    pub fn cloc_max(&self) -> f64 {
451        self.cloc.cloc_max()
452    }
453
454    /// The `Ploc` metric minimum value.
455    #[inline(always)]
456    pub fn ploc_min(&self) -> f64 {
457        self.ploc.ploc_min()
458    }
459
460    /// The `Ploc` metric maximum value.
461    #[inline(always)]
462    pub fn ploc_max(&self) -> f64 {
463        self.ploc.ploc_max()
464    }
465
466    /// The `Lloc` metric minimum value.
467    #[inline(always)]
468    pub fn lloc_min(&self) -> f64 {
469        self.lloc.lloc_min()
470    }
471
472    /// The `Lloc` metric maximum value.
473    #[inline(always)]
474    pub fn lloc_max(&self) -> f64 {
475        self.lloc.lloc_max()
476    }
477
478    /// The `Blank` metric minimum value.
479    #[inline(always)]
480    pub fn blank_min(&self) -> f64 {
481        self.blank_min as f64
482    }
483
484    /// The `Blank` metric maximum value.
485    #[inline(always)]
486    pub fn blank_max(&self) -> f64 {
487        self.blank_max as f64
488    }
489
490    #[inline(always)]
491    pub(crate) fn compute_minmax(&mut self) {
492        self.sloc.compute_minmax();
493        self.ploc.compute_minmax();
494        self.cloc.compute_minmax();
495        self.lloc.compute_minmax();
496
497        if self.blank_min == usize::MAX {
498            self.blank_min = self.blank_min.min(self.blank() as usize);
499            self.blank_max = self.blank_max.max(self.blank() as usize);
500        }
501    }
502}
503
504pub trait Loc
505where
506    Self: Checker,
507{
508    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool);
509}
510
511#[inline(always)]
512fn init(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) -> (usize, usize) {
513    let start = node.start_row();
514    let end = node.end_row();
515
516    if is_func_space {
517        stats.sloc.start = start;
518        stats.sloc.end = end;
519        stats.sloc.unit = is_unit;
520    }
521    (start, end)
522}
523
524#[inline(always)]
525// Discriminates among the comments that are *after* a code line and
526// the ones that are on an independent line.
527// This difference is necessary in order to avoid having
528// a wrong count for the blank metric.
529fn add_cloc_lines(stats: &mut Stats, start: usize, end: usize) {
530    let comment_diff = end - start;
531    let is_comment_after_code_line = stats.ploc.lines.contains(&start);
532    if is_comment_after_code_line && comment_diff == 0 {
533        // A comment is *entirely* next to a code line
534        stats.cloc.code_comment_lines += 1;
535    } else if is_comment_after_code_line && comment_diff > 0 {
536        // A block comment that starts next to a code line and ends on
537        // independent lines.
538        stats.cloc.code_comment_lines += 1;
539        stats.cloc.only_comment_lines += comment_diff;
540    } else {
541        // A comment on an independent line AND
542        // a block comment on independent lines OR
543        // a comment *before* a code line
544        stats.cloc.only_comment_lines += (end - start) + 1;
545        // Save line end of a comment to check whether
546        // a comment *before* a code line is considered
547        stats.cloc.comment_line_end = Some(end);
548    }
549}
550
551#[inline(always)]
552// Detects the comments that are on a code line but *before* the code part.
553// This difference is necessary in order to avoid having
554// a wrong count for the blank metric.
555fn check_comment_ends_on_code_line(stats: &mut Stats, start_code_line: usize) {
556    if let Some(end) = stats.cloc.comment_line_end
557        && end == start_code_line
558        && !stats.ploc.lines.contains(&start_code_line)
559    {
560        // Comment entirely *before* a code line
561        stats.cloc.only_comment_lines -= 1;
562        stats.cloc.code_comment_lines += 1;
563    }
564}
565
566impl Loc for PythonCode {
567    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
568        use Python::*;
569
570        let (start, end) = init(node, stats, is_func_space, is_unit);
571
572        match node.kind_id().into() {
573            StringStart | StringEnd | StringContent | Block | Module => {}
574            Comment => {
575                add_cloc_lines(stats, start, end);
576            }
577            String => {
578                let parent = node.parent().unwrap();
579                if let ExpressionStatement = parent.kind_id().into() {
580                    add_cloc_lines(stats, start, end);
581                } else if parent.start_row() != start {
582                    check_comment_ends_on_code_line(stats, start);
583                    stats.ploc.lines.insert(start);
584                }
585            }
586            Statement
587            | SimpleStatements
588            | ImportStatement
589            | FutureImportStatement
590            | ImportFromStatement
591            | PrintStatement
592            | AssertStatement
593            | ReturnStatement
594            | DeleteStatement
595            | RaiseStatement
596            | PassStatement
597            | BreakStatement
598            | ContinueStatement
599            | IfStatement
600            | ForStatement
601            | WhileStatement
602            | TryStatement
603            | WithStatement
604            | GlobalStatement
605            | NonlocalStatement
606            | ExecStatement
607            | ExpressionStatement => {
608                stats.lloc.logical_lines += 1;
609            }
610            _ => {
611                check_comment_ends_on_code_line(stats, start);
612                stats.ploc.lines.insert(start);
613            }
614        }
615    }
616}
617
618impl Loc for JavascriptCode {
619    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
620        use Javascript::*;
621
622        let (start, end) = init(node, stats, is_func_space, is_unit);
623
624        match node.kind_id().into() {
625            String | DQUOTE | Program => {}
626            Comment => {
627                add_cloc_lines(stats, start, end);
628            }
629            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
630            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
631            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
632            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
633            | StatementIdentifier => {
634                stats.lloc.logical_lines += 1;
635            }
636            _ => {
637                check_comment_ends_on_code_line(stats, start);
638                stats.ploc.lines.insert(start);
639            }
640        }
641    }
642}
643
644impl Loc for TypescriptCode {
645    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
646        use Typescript::*;
647
648        let (start, end) = init(node, stats, is_func_space, is_unit);
649
650        match node.kind_id().into() {
651            String | DQUOTE | Program => {}
652            Comment => {
653                add_cloc_lines(stats, start, end);
654            }
655            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
656            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
657            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
658            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
659            | StatementIdentifier => {
660                stats.lloc.logical_lines += 1;
661            }
662            _ => {
663                check_comment_ends_on_code_line(stats, start);
664                stats.ploc.lines.insert(start);
665            }
666        }
667    }
668}
669
670impl Loc for TsxCode {
671    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
672        use Tsx::*;
673
674        let (start, end) = init(node, stats, is_func_space, is_unit);
675
676        match node.kind_id().into() {
677            String | DQUOTE | Program => {}
678            Comment => {
679                add_cloc_lines(stats, start, end);
680            }
681            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
682            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
683            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
684            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
685            | StatementIdentifier => {
686                stats.lloc.logical_lines += 1;
687            }
688            _ => {
689                check_comment_ends_on_code_line(stats, start);
690                stats.ploc.lines.insert(start);
691            }
692        }
693    }
694}
695
696impl Loc for RustCode {
697    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
698        use Rust::*;
699
700        let (start, end) = init(node, stats, is_func_space, is_unit);
701
702        match node.kind_id().into() {
703            StringLiteral
704            | RawStringLiteral
705            | Block
706            | SourceFile
707            | SLASH
708            | SLASHSLASH
709            | SLASHSTAR
710            | STARSLASH
711            | OuterDocCommentMarker
712            | OuterDocCommentMarker2
713            | DocComment
714            | InnerDocCommentMarker
715            | BANG => {}
716            BlockComment => {
717                add_cloc_lines(stats, start, end);
718            }
719            LineComment => {
720                // Exclude the last line for `LineComment` containing a `DocComment`,
721                // since the `DocComment` includes the newline,
722                // as explained here: https://github.com/tree-sitter/tree-sitter-rust/blob/2eaf126458a4d6a69401089b6ba78c5e5d6c1ced/src/scanner.c#L194-L195
723                let end = if node.is_child(DocComment as u16) {
724                    end - 1
725                } else {
726                    end
727                };
728                add_cloc_lines(stats, start, end);
729            }
730            Statement
731            | EmptyStatement
732            | ExpressionStatement
733            | LetDeclaration
734            | AssignmentExpression
735            | CompoundAssignmentExpr => {
736                stats.lloc.logical_lines += 1;
737            }
738            _ => {
739                check_comment_ends_on_code_line(stats, start);
740                stats.ploc.lines.insert(start);
741            }
742        }
743    }
744}
745
746impl Loc for CppCode {
747    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
748        use Cpp::*;
749
750        let (start, end) = init(node, stats, is_func_space, is_unit);
751
752        match node.kind_id().into() {
753            RawStringLiteral | StringLiteral | DeclarationList | FieldDeclarationList
754            | TranslationUnit => {}
755            Comment => {
756                add_cloc_lines(stats, start, end);
757            }
758            WhileStatement | SwitchStatement | CaseStatement | IfStatement | ForStatement
759            | ReturnStatement | BreakStatement | ContinueStatement | GotoStatement
760            | ThrowStatement | TryStatement | TryStatement2 | ExpressionStatement
761            | ExpressionStatement2 | LabeledStatement | StatementIdentifier => {
762                stats.lloc.logical_lines += 1;
763            }
764            Declaration => {
765                if node.count_specific_ancestors::<CppParser>(
766                    |node| {
767                        matches!(
768                            node.kind_id().into(),
769                            WhileStatement | ForStatement | IfStatement
770                        )
771                    },
772                    |node| node.kind_id() == CompoundStatement,
773                ) == 0
774                {
775                    stats.lloc.logical_lines += 1;
776                }
777            }
778            _ => {
779                check_comment_ends_on_code_line(stats, start);
780                stats.ploc.lines.insert(start);
781
782                // As reported here: https://github.com/tree-sitter/tree-sitter-cpp/issues/276
783                // `tree-sitter-cpp` doesn't expand macros, providing a single `PreprocArg` node for the entire macro argument.
784                // Therefore, all lines from `start_row` to `end_row` must be added to PLOC to account for the unexpanded macro content
785                if let PreprocArg = node.kind_id().into() {
786                    (node.start_row() + 1..=node.end_row()).for_each(|line| {
787                        stats.ploc.lines.insert(line);
788                    });
789                }
790            }
791        }
792    }
793}
794
795impl Loc for JavaCode {
796    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
797        use Java::*;
798
799        let (start, end) = init(node, stats, is_func_space, is_unit);
800        let kind_id: Java = node.kind_id().into();
801        // LLOC in Java is counted for statements only
802        // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
803        match kind_id {
804            Program => {}
805            LineComment | BlockComment => {
806                add_cloc_lines(stats, start, end);
807            }
808            AssertStatement | BreakStatement | ContinueStatement | DoStatement
809            | EnhancedForStatement | ExpressionStatement | ForStatement | IfStatement
810            | ReturnStatement | SwitchExpression | ThrowStatement | TryStatement
811            | WhileStatement => {
812                stats.lloc.logical_lines += 1;
813            }
814            LocalVariableDeclaration => {
815                if node.count_specific_ancestors::<JavaParser>(
816                    |node| node.kind_id() == ForStatement,
817                    |node| node.kind_id() == Block,
818                ) == 0
819                {
820                    // The initializer, condition, and increment in a for loop are expressions.
821                    // Don't count the variable declaration if in a ForStatement.
822                    // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
823                    stats.lloc.logical_lines += 1;
824                }
825            }
826            _ => {
827                check_comment_ends_on_code_line(stats, start);
828                stats.ploc.lines.insert(start);
829            }
830        }
831    }
832}
833
834implement_metric_trait!(Loc, PreprocCode, CcommentCode, KotlinCode);
835
836#[cfg(test)]
837mod tests {
838    use crate::tools::check_metrics;
839
840    use super::*;
841
842    #[test]
843    fn python_sloc() {
844        check_metrics::<PythonParser>(
845            "
846
847            a = 42
848
849            ",
850            "foo.py",
851            |metric| {
852                // Spaces: 1
853                insta::assert_json_snapshot!(
854                    metric.loc,
855                    @r###"
856                    {
857                      "sloc": 1.0,
858                      "ploc": 1.0,
859                      "lloc": 1.0,
860                      "cloc": 0.0,
861                      "blank": 0.0,
862                      "sloc_average": 1.0,
863                      "ploc_average": 1.0,
864                      "lloc_average": 1.0,
865                      "cloc_average": 0.0,
866                      "blank_average": 0.0,
867                      "sloc_min": 1.0,
868                      "sloc_max": 1.0,
869                      "cloc_min": 0.0,
870                      "cloc_max": 0.0,
871                      "ploc_min": 1.0,
872                      "ploc_max": 1.0,
873                      "lloc_min": 1.0,
874                      "lloc_max": 1.0,
875                      "blank_min": 0.0,
876                      "blank_max": 0.0
877                    }"###
878                );
879            },
880        );
881    }
882
883    #[test]
884    fn python_blank() {
885        check_metrics::<PythonParser>(
886            "
887            a = 42
888
889            b = 43
890
891            ",
892            "foo.py",
893            |metric| {
894                // Spaces: 1
895                insta::assert_json_snapshot!(
896                    metric.loc,
897                    @r###"
898                    {
899                      "sloc": 3.0,
900                      "ploc": 2.0,
901                      "lloc": 2.0,
902                      "cloc": 0.0,
903                      "blank": 1.0,
904                      "sloc_average": 3.0,
905                      "ploc_average": 2.0,
906                      "lloc_average": 2.0,
907                      "cloc_average": 0.0,
908                      "blank_average": 1.0,
909                      "sloc_min": 3.0,
910                      "sloc_max": 3.0,
911                      "cloc_min": 0.0,
912                      "cloc_max": 0.0,
913                      "ploc_min": 2.0,
914                      "ploc_max": 2.0,
915                      "lloc_min": 2.0,
916                      "lloc_max": 2.0,
917                      "blank_min": 1.0,
918                      "blank_max": 1.0
919                    }"###
920                );
921            },
922        );
923    }
924
925    #[test]
926    fn rust_blank() {
927        check_metrics::<RustParser>(
928            "
929
930            let a = 42;
931
932            let b = 43;
933
934            ",
935            "foo.rs",
936            |metric| {
937                // Spaces: 1
938                insta::assert_json_snapshot!(
939                    metric.loc,
940                    @r###"
941                    {
942                      "sloc": 3.0,
943                      "ploc": 2.0,
944                      "lloc": 2.0,
945                      "cloc": 0.0,
946                      "blank": 1.0,
947                      "sloc_average": 3.0,
948                      "ploc_average": 2.0,
949                      "lloc_average": 2.0,
950                      "cloc_average": 0.0,
951                      "blank_average": 1.0,
952                      "sloc_min": 3.0,
953                      "sloc_max": 3.0,
954                      "cloc_min": 0.0,
955                      "cloc_max": 0.0,
956                      "ploc_min": 2.0,
957                      "ploc_max": 2.0,
958                      "lloc_min": 2.0,
959                      "lloc_max": 2.0,
960                      "blank_min": 1.0,
961                      "blank_max": 1.0
962                    }"###
963                );
964            },
965        );
966
967        check_metrics::<RustParser>("fn func() { /* comment */ }", "foo.rs", |metric| {
968            // Spaces: 2
969            insta::assert_json_snapshot!(
970                metric.loc,
971                @r###"
972                    {
973                      "sloc": 1.0,
974                      "ploc": 1.0,
975                      "lloc": 0.0,
976                      "cloc": 1.0,
977                      "blank": 0.0,
978                      "sloc_average": 0.5,
979                      "ploc_average": 0.5,
980                      "lloc_average": 0.0,
981                      "cloc_average": 0.5,
982                      "blank_average": 0.0,
983                      "sloc_min": 1.0,
984                      "sloc_max": 1.0,
985                      "cloc_min": 1.0,
986                      "cloc_max": 1.0,
987                      "ploc_min": 1.0,
988                      "ploc_max": 1.0,
989                      "lloc_min": 0.0,
990                      "lloc_max": 0.0,
991                      "blank_min": 0.0,
992                      "blank_max": 0.0
993                    }"###
994            );
995        });
996    }
997
998    #[test]
999    fn c_blank() {
1000        check_metrics::<CppParser>(
1001            "
1002
1003            int a = 42;
1004
1005            int b = 43;
1006
1007            ",
1008            "foo.c",
1009            |metric| {
1010                // Spaces: 1
1011                insta::assert_json_snapshot!(
1012                    metric.loc,
1013                    @r###"
1014                    {
1015                      "sloc": 3.0,
1016                      "ploc": 2.0,
1017                      "lloc": 2.0,
1018                      "cloc": 0.0,
1019                      "blank": 1.0,
1020                      "sloc_average": 3.0,
1021                      "ploc_average": 2.0,
1022                      "lloc_average": 2.0,
1023                      "cloc_average": 0.0,
1024                      "blank_average": 1.0,
1025                      "sloc_min": 3.0,
1026                      "sloc_max": 3.0,
1027                      "cloc_min": 0.0,
1028                      "cloc_max": 0.0,
1029                      "ploc_min": 2.0,
1030                      "ploc_max": 2.0,
1031                      "lloc_min": 2.0,
1032                      "lloc_max": 2.0,
1033                      "blank_min": 1.0,
1034                      "blank_max": 1.0
1035                    }"###
1036                );
1037            },
1038        );
1039    }
1040
1041    #[test]
1042    fn python_no_zero_blank() {
1043        // Checks that the blank metric is not equal to 0 when there are some
1044        // comments next to code lines.
1045        check_metrics::<PythonParser>(
1046            "def ConnectToUpdateServer():
1047                 pool = 4
1048
1049                 updateServer = -42
1050                 isConnected = False
1051                 currTry = 0
1052                 numRetries = 10 # Number of IPC connection retries before
1053                                 # giving up.
1054                 numTries = 20 # Number of IPC connection tries before
1055                               # giving up.",
1056            "foo.py",
1057            |metric| {
1058                // Spaces: 2
1059                insta::assert_json_snapshot!(
1060                    metric.loc,
1061                    @r###"
1062                    {
1063                      "sloc": 10.0,
1064                      "ploc": 7.0,
1065                      "lloc": 6.0,
1066                      "cloc": 4.0,
1067                      "blank": 1.0,
1068                      "sloc_average": 5.0,
1069                      "ploc_average": 3.5,
1070                      "lloc_average": 3.0,
1071                      "cloc_average": 2.0,
1072                      "blank_average": 0.5,
1073                      "sloc_min": 10.0,
1074                      "sloc_max": 10.0,
1075                      "cloc_min": 4.0,
1076                      "cloc_max": 4.0,
1077                      "ploc_min": 7.0,
1078                      "ploc_max": 7.0,
1079                      "lloc_min": 6.0,
1080                      "lloc_max": 6.0,
1081                      "blank_min": 1.0,
1082                      "blank_max": 1.0
1083                    }"###
1084                );
1085            },
1086        );
1087    }
1088
1089    #[test]
1090    fn python_no_blank() {
1091        // Checks that the blank metric is equal to 0 when there are no blank
1092        // lines and there are comments next to code lines.
1093        check_metrics::<PythonParser>(
1094            "def ConnectToUpdateServer():
1095                 pool = 4
1096                 updateServer = -42
1097                 isConnected = False
1098                 currTry = 0
1099                 numRetries = 10 # Number of IPC connection retries before
1100                                 # giving up.
1101                 numTries = 20 # Number of IPC connection tries before
1102                               # giving up.",
1103            "foo.py",
1104            |metric| {
1105                // Spaces: 2
1106                insta::assert_json_snapshot!(
1107                    metric.loc,
1108                    @r###"
1109                    {
1110                      "sloc": 9.0,
1111                      "ploc": 7.0,
1112                      "lloc": 6.0,
1113                      "cloc": 4.0,
1114                      "blank": 0.0,
1115                      "sloc_average": 4.5,
1116                      "ploc_average": 3.5,
1117                      "lloc_average": 3.0,
1118                      "cloc_average": 2.0,
1119                      "blank_average": 0.0,
1120                      "sloc_min": 9.0,
1121                      "sloc_max": 9.0,
1122                      "cloc_min": 4.0,
1123                      "cloc_max": 4.0,
1124                      "ploc_min": 7.0,
1125                      "ploc_max": 7.0,
1126                      "lloc_min": 6.0,
1127                      "lloc_max": 6.0,
1128                      "blank_min": 0.0,
1129                      "blank_max": 0.0
1130                    }"###
1131                );
1132            },
1133        );
1134    }
1135
1136    #[test]
1137    fn python_no_zero_blank_more_comments() {
1138        // Checks that the blank metric is not equal to 0 when there are more
1139        // comments next to code lines compared to the previous tests.
1140        check_metrics::<PythonParser>(
1141            "def ConnectToUpdateServer():
1142                 pool = 4
1143
1144                 updateServer = -42
1145                 isConnected = False
1146                 currTry = 0 # Set this variable to 0
1147                 numRetries = 10 # Number of IPC connection retries before
1148                                 # giving up.
1149                 numTries = 20 # Number of IPC connection tries before
1150                               # giving up.",
1151            "foo.py",
1152            |metric| {
1153                // Spaces: 2
1154                insta::assert_json_snapshot!(
1155                    metric.loc,
1156                    @r###"
1157                    {
1158                      "sloc": 10.0,
1159                      "ploc": 7.0,
1160                      "lloc": 6.0,
1161                      "cloc": 5.0,
1162                      "blank": 1.0,
1163                      "sloc_average": 5.0,
1164                      "ploc_average": 3.5,
1165                      "lloc_average": 3.0,
1166                      "cloc_average": 2.5,
1167                      "blank_average": 0.5,
1168                      "sloc_min": 10.0,
1169                      "sloc_max": 10.0,
1170                      "cloc_min": 5.0,
1171                      "cloc_max": 5.0,
1172                      "ploc_min": 7.0,
1173                      "ploc_max": 7.0,
1174                      "lloc_min": 6.0,
1175                      "lloc_max": 6.0,
1176                      "blank_min": 1.0,
1177                      "blank_max": 1.0
1178                    }"###
1179                );
1180            },
1181        );
1182    }
1183
1184    #[test]
1185    fn rust_no_zero_blank() {
1186        // Checks that the blank metric is not equal to 0 when there are some
1187        // comments next to code lines.
1188        check_metrics::<RustParser>(
1189            "fn ConnectToUpdateServer() {
1190              let pool = 0;
1191
1192              let updateServer = -42;
1193              let isConnected = false;
1194              let currTry = 0;
1195              let numRetries = 10;  // Number of IPC connection retries before
1196                                    // giving up.
1197              let numTries = 20;    // Number of IPC connection tries before
1198                                    // giving up.
1199            }",
1200            "foo.rs",
1201            |metric| {
1202                // Spaces: 2
1203                insta::assert_json_snapshot!(
1204                    metric.loc,
1205                    @r###"
1206                    {
1207                      "sloc": 11.0,
1208                      "ploc": 8.0,
1209                      "lloc": 6.0,
1210                      "cloc": 4.0,
1211                      "blank": 1.0,
1212                      "sloc_average": 5.5,
1213                      "ploc_average": 4.0,
1214                      "lloc_average": 3.0,
1215                      "cloc_average": 2.0,
1216                      "blank_average": 0.5,
1217                      "sloc_min": 11.0,
1218                      "sloc_max": 11.0,
1219                      "cloc_min": 4.0,
1220                      "cloc_max": 4.0,
1221                      "ploc_min": 8.0,
1222                      "ploc_max": 8.0,
1223                      "lloc_min": 6.0,
1224                      "lloc_max": 6.0,
1225                      "blank_min": 1.0,
1226                      "blank_max": 1.0
1227                    }"###
1228                );
1229            },
1230        );
1231    }
1232
1233    #[test]
1234    fn javascript_no_zero_blank() {
1235        // Checks that the blank metric is not equal to 0 when there are some
1236        // comments next to code lines.
1237        check_metrics::<JavascriptParser>(
1238            "function ConnectToUpdateServer() {
1239              var pool = 0;
1240
1241              var updateServer = -42;
1242              var isConnected = false;
1243              var currTry = 0;
1244              var numRetries = 10;  // Number of IPC connection retries before
1245                                    // giving up.
1246              var numTries = 20;    // Number of IPC connection tries before
1247                                    // giving up.
1248            }",
1249            "foo.js",
1250            |metric| {
1251                // Spaces: 2
1252                insta::assert_json_snapshot!(
1253                    metric.loc,
1254                    @r###"
1255                    {
1256                      "sloc": 11.0,
1257                      "ploc": 8.0,
1258                      "lloc": 1.0,
1259                      "cloc": 4.0,
1260                      "blank": 1.0,
1261                      "sloc_average": 5.5,
1262                      "ploc_average": 4.0,
1263                      "lloc_average": 0.5,
1264                      "cloc_average": 2.0,
1265                      "blank_average": 0.5,
1266                      "sloc_min": 11.0,
1267                      "sloc_max": 11.0,
1268                      "cloc_min": 4.0,
1269                      "cloc_max": 4.0,
1270                      "ploc_min": 8.0,
1271                      "ploc_max": 8.0,
1272                      "lloc_min": 1.0,
1273                      "lloc_max": 1.0,
1274                      "blank_min": 1.0,
1275                      "blank_max": 1.0
1276                    }"###
1277                );
1278            },
1279        );
1280    }
1281
1282    #[test]
1283    fn cpp_no_zero_blank() {
1284        // Checks that the blank metric is not equal to 0 when there are some
1285        // comments next to code lines.
1286        check_metrics::<CppParser>(
1287            "void ConnectToUpdateServer() {
1288              int pool;
1289
1290              int updateServer = -42;
1291              bool isConnected = false;
1292              int currTry = 0;
1293              const int numRetries = 10; // Number of IPC connection retries before
1294                                         // giving up.
1295              const int numTries = 20; // Number of IPC connection tries before
1296                                       // giving up.
1297            }",
1298            "foo.cpp",
1299            |metric| {
1300                // Spaces: 2
1301                insta::assert_json_snapshot!(
1302                    metric.loc,
1303                    @r###"
1304                    {
1305                      "sloc": 11.0,
1306                      "ploc": 8.0,
1307                      "lloc": 6.0,
1308                      "cloc": 4.0,
1309                      "blank": 1.0,
1310                      "sloc_average": 5.5,
1311                      "ploc_average": 4.0,
1312                      "lloc_average": 3.0,
1313                      "cloc_average": 2.0,
1314                      "blank_average": 0.5,
1315                      "sloc_min": 11.0,
1316                      "sloc_max": 11.0,
1317                      "cloc_min": 4.0,
1318                      "cloc_max": 4.0,
1319                      "ploc_min": 8.0,
1320                      "ploc_max": 8.0,
1321                      "lloc_min": 6.0,
1322                      "lloc_max": 6.0,
1323                      "blank_min": 1.0,
1324                      "blank_max": 1.0
1325                    }"###
1326                );
1327            },
1328        );
1329    }
1330
1331    #[test]
1332    fn cpp_code_line_start_block_blank() {
1333        // Checks that the blank metric is equal to 1 when there are
1334        // block comments starting next to code lines.
1335        check_metrics::<CppParser>(
1336            "void ConnectToUpdateServer() {
1337              int pool;
1338
1339              int updateServer = -42;
1340              bool isConnected = false;
1341              int currTry = 0;
1342              const int numRetries = 10; /* Number of IPC connection retries
1343              before
1344              giving up. */
1345              const int numTries = 20; // Number of IPC connection tries before
1346                                       // giving up.
1347            }",
1348            "foo.cpp",
1349            |metric| {
1350                // Spaces: 2
1351                insta::assert_json_snapshot!(
1352                    metric.loc,
1353                    @r###"
1354                    {
1355                      "sloc": 12.0,
1356                      "ploc": 8.0,
1357                      "lloc": 6.0,
1358                      "cloc": 5.0,
1359                      "blank": 1.0,
1360                      "sloc_average": 6.0,
1361                      "ploc_average": 4.0,
1362                      "lloc_average": 3.0,
1363                      "cloc_average": 2.5,
1364                      "blank_average": 0.5,
1365                      "sloc_min": 12.0,
1366                      "sloc_max": 12.0,
1367                      "cloc_min": 5.0,
1368                      "cloc_max": 5.0,
1369                      "ploc_min": 8.0,
1370                      "ploc_max": 8.0,
1371                      "lloc_min": 6.0,
1372                      "lloc_max": 6.0,
1373                      "blank_min": 1.0,
1374                      "blank_max": 1.0
1375                    }"###
1376                );
1377            },
1378        );
1379    }
1380
1381    #[test]
1382    fn cpp_block_comment_blank() {
1383        // Checks that the blank metric is equal to 1 when there are
1384        // block comments on independent lines.
1385        check_metrics::<CppParser>(
1386            "void ConnectToUpdateServer() {
1387              int pool;
1388
1389              int updateServer = -42;
1390              bool isConnected = false;
1391              int currTry = 0;
1392              /* Number of IPC connection retries
1393              before
1394              giving up. */
1395              const int numRetries = 10;
1396              const int numTries = 20; // Number of IPC connection tries before
1397                                       // giving up.
1398            }",
1399            "foo.cpp",
1400            |metric| {
1401                // Spaces: 2
1402                insta::assert_json_snapshot!(
1403                    metric.loc,
1404                    @r###"
1405                    {
1406                      "sloc": 13.0,
1407                      "ploc": 8.0,
1408                      "lloc": 6.0,
1409                      "cloc": 5.0,
1410                      "blank": 1.0,
1411                      "sloc_average": 6.5,
1412                      "ploc_average": 4.0,
1413                      "lloc_average": 3.0,
1414                      "cloc_average": 2.5,
1415                      "blank_average": 0.5,
1416                      "sloc_min": 13.0,
1417                      "sloc_max": 13.0,
1418                      "cloc_min": 5.0,
1419                      "cloc_max": 5.0,
1420                      "ploc_min": 8.0,
1421                      "ploc_max": 8.0,
1422                      "lloc_min": 6.0,
1423                      "lloc_max": 6.0,
1424                      "blank_min": 1.0,
1425                      "blank_max": 1.0
1426                    }"###
1427                );
1428            },
1429        );
1430    }
1431
1432    #[test]
1433    fn cpp_code_line_block_one_line_blank() {
1434        // Checks that the blank metric is equal to 1 when there are
1435        // block comments before the same code line.
1436        check_metrics::<CppParser>(
1437            "void ConnectToUpdateServer() {
1438              int pool;
1439
1440              int updateServer = -42;
1441              bool isConnected = false;
1442              int currTry = 0;
1443              /* Number of IPC connection retries before giving up. */ const int numRetries = 10;
1444              const int numTries = 20; // Number of IPC connection tries before
1445                                       // giving up.
1446            }",
1447            "foo.cpp",
1448            |metric| {
1449                // Spaces: 2
1450                insta::assert_json_snapshot!(
1451                    metric.loc,
1452                    @r###"
1453                    {
1454                      "sloc": 10.0,
1455                      "ploc": 8.0,
1456                      "lloc": 6.0,
1457                      "cloc": 3.0,
1458                      "blank": 1.0,
1459                      "sloc_average": 5.0,
1460                      "ploc_average": 4.0,
1461                      "lloc_average": 3.0,
1462                      "cloc_average": 1.5,
1463                      "blank_average": 0.5,
1464                      "sloc_min": 10.0,
1465                      "sloc_max": 10.0,
1466                      "cloc_min": 3.0,
1467                      "cloc_max": 3.0,
1468                      "ploc_min": 8.0,
1469                      "ploc_max": 8.0,
1470                      "lloc_min": 6.0,
1471                      "lloc_max": 6.0,
1472                      "blank_min": 1.0,
1473                      "blank_max": 1.0
1474                    }"###
1475                );
1476            },
1477        );
1478    }
1479
1480    #[test]
1481    fn cpp_code_line_end_block_blank() {
1482        // Checks that the blank metric is equal to 1 when there are
1483        // block comments ending next to code lines.
1484        check_metrics::<CppParser>(
1485            "void ConnectToUpdateServer() {
1486              int pool;
1487
1488              int updateServer = -42;
1489              bool isConnected = false;
1490              int currTry = 0;
1491              /* Number of IPC connection retries
1492              before
1493              giving up. */ const int numRetries = 10;
1494              const int numTries = 20; // Number of IPC connection tries before
1495                                       // giving up.
1496            }",
1497            "foo.cpp",
1498            |metric| {
1499                // Spaces: 2
1500                insta::assert_json_snapshot!(
1501                    metric.loc,
1502                    @r###"
1503                    {
1504                      "sloc": 12.0,
1505                      "ploc": 8.0,
1506                      "lloc": 6.0,
1507                      "cloc": 5.0,
1508                      "blank": 1.0,
1509                      "sloc_average": 6.0,
1510                      "ploc_average": 4.0,
1511                      "lloc_average": 3.0,
1512                      "cloc_average": 2.5,
1513                      "blank_average": 0.5,
1514                      "sloc_min": 12.0,
1515                      "sloc_max": 12.0,
1516                      "cloc_min": 5.0,
1517                      "cloc_max": 5.0,
1518                      "ploc_min": 8.0,
1519                      "ploc_max": 8.0,
1520                      "lloc_min": 6.0,
1521                      "lloc_max": 6.0,
1522                      "blank_min": 1.0,
1523                      "blank_max": 1.0
1524                    }"###
1525                );
1526            },
1527        );
1528    }
1529
1530    #[test]
1531    fn python_cloc() {
1532        check_metrics::<PythonParser>(
1533            "\"\"\"Block comment
1534            Block comment
1535            \"\"\"
1536            # Line Comment
1537            a = 42 # Line Comment",
1538            "foo.py",
1539            |metric| {
1540                // Spaces: 1
1541                insta::assert_json_snapshot!(
1542                    metric.loc,
1543                    @r###"
1544                    {
1545                      "sloc": 5.0,
1546                      "ploc": 1.0,
1547                      "lloc": 2.0,
1548                      "cloc": 5.0,
1549                      "blank": 0.0,
1550                      "sloc_average": 5.0,
1551                      "ploc_average": 1.0,
1552                      "lloc_average": 2.0,
1553                      "cloc_average": 5.0,
1554                      "blank_average": 0.0,
1555                      "sloc_min": 5.0,
1556                      "sloc_max": 5.0,
1557                      "cloc_min": 5.0,
1558                      "cloc_max": 5.0,
1559                      "ploc_min": 1.0,
1560                      "ploc_max": 1.0,
1561                      "lloc_min": 2.0,
1562                      "lloc_max": 2.0,
1563                      "blank_min": 0.0,
1564                      "blank_max": 0.0
1565                    }"###
1566                );
1567            },
1568        );
1569    }
1570
1571    #[test]
1572    fn rust_cloc() {
1573        check_metrics::<RustParser>(
1574            "/*Block comment
1575            Block Comment*/
1576            //Line Comment
1577            /*Block Comment*/ let a = 42; // Line Comment",
1578            "foo.rs",
1579            |metric| {
1580                // Spaces: 1
1581                insta::assert_json_snapshot!(
1582                    metric.loc,
1583                    @r###"
1584                    {
1585                      "sloc": 4.0,
1586                      "ploc": 1.0,
1587                      "lloc": 1.0,
1588                      "cloc": 5.0,
1589                      "blank": 0.0,
1590                      "sloc_average": 4.0,
1591                      "ploc_average": 1.0,
1592                      "lloc_average": 1.0,
1593                      "cloc_average": 5.0,
1594                      "blank_average": 0.0,
1595                      "sloc_min": 4.0,
1596                      "sloc_max": 4.0,
1597                      "cloc_min": 5.0,
1598                      "cloc_max": 5.0,
1599                      "ploc_min": 1.0,
1600                      "ploc_max": 1.0,
1601                      "lloc_min": 1.0,
1602                      "lloc_max": 1.0,
1603                      "blank_min": 0.0,
1604                      "blank_max": 0.0
1605                    }"###
1606                );
1607            },
1608        );
1609    }
1610
1611    #[test]
1612    fn c_cloc() {
1613        check_metrics::<CppParser>(
1614            "/*Block comment
1615            Block Comment*/
1616            //Line Comment
1617            /*Block Comment*/ int a = 42; // Line Comment",
1618            "foo.c",
1619            |metric| {
1620                // Spaces: 1
1621                insta::assert_json_snapshot!(
1622                    metric.loc,
1623                    @r###"
1624                    {
1625                      "sloc": 4.0,
1626                      "ploc": 1.0,
1627                      "lloc": 1.0,
1628                      "cloc": 5.0,
1629                      "blank": 0.0,
1630                      "sloc_average": 4.0,
1631                      "ploc_average": 1.0,
1632                      "lloc_average": 1.0,
1633                      "cloc_average": 5.0,
1634                      "blank_average": 0.0,
1635                      "sloc_min": 4.0,
1636                      "sloc_max": 4.0,
1637                      "cloc_min": 5.0,
1638                      "cloc_max": 5.0,
1639                      "ploc_min": 1.0,
1640                      "ploc_max": 1.0,
1641                      "lloc_min": 1.0,
1642                      "lloc_max": 1.0,
1643                      "blank_min": 0.0,
1644                      "blank_max": 0.0
1645                    }"###
1646                );
1647            },
1648        );
1649    }
1650
1651    #[test]
1652    fn python_lloc() {
1653        check_metrics::<PythonParser>(
1654            "for x in range(0,42):
1655                if x % 2 == 0:
1656                    print(x)",
1657            "foo.py",
1658            |metric| {
1659                // Spaces: 1
1660                insta::assert_json_snapshot!(
1661                    metric.loc,
1662                    @r###"
1663                    {
1664                      "sloc": 3.0,
1665                      "ploc": 3.0,
1666                      "lloc": 3.0,
1667                      "cloc": 0.0,
1668                      "blank": 0.0,
1669                      "sloc_average": 3.0,
1670                      "ploc_average": 3.0,
1671                      "lloc_average": 3.0,
1672                      "cloc_average": 0.0,
1673                      "blank_average": 0.0,
1674                      "sloc_min": 3.0,
1675                      "sloc_max": 3.0,
1676                      "cloc_min": 0.0,
1677                      "cloc_max": 0.0,
1678                      "ploc_min": 3.0,
1679                      "ploc_max": 3.0,
1680                      "lloc_min": 3.0,
1681                      "lloc_max": 3.0,
1682                      "blank_min": 0.0,
1683                      "blank_max": 0.0
1684                    }"###
1685                );
1686            },
1687        );
1688    }
1689
1690    #[test]
1691    fn rust_lloc() {
1692        check_metrics::<RustParser>(
1693            "for x in 0..42 {
1694                if x % 2 == 0 {
1695                    println!(\"{}\", x);
1696                }
1697             }",
1698            "foo.rs",
1699            |metric| {
1700                // Spaces: 1
1701                insta::assert_json_snapshot!(
1702                    metric.loc,
1703                    @r###"
1704                    {
1705                      "sloc": 5.0,
1706                      "ploc": 5.0,
1707                      "lloc": 3.0,
1708                      "cloc": 0.0,
1709                      "blank": 0.0,
1710                      "sloc_average": 5.0,
1711                      "ploc_average": 5.0,
1712                      "lloc_average": 3.0,
1713                      "cloc_average": 0.0,
1714                      "blank_average": 0.0,
1715                      "sloc_min": 5.0,
1716                      "sloc_max": 5.0,
1717                      "cloc_min": 0.0,
1718                      "cloc_max": 0.0,
1719                      "ploc_min": 5.0,
1720                      "ploc_max": 5.0,
1721                      "lloc_min": 3.0,
1722                      "lloc_max": 3.0,
1723                      "blank_min": 0.0,
1724                      "blank_max": 0.0
1725                    }"###
1726                );
1727            },
1728        );
1729
1730        // LLOC returns three because there is an empty Rust statement
1731        check_metrics::<RustParser>(
1732            "let a = 42;
1733             if true {
1734                42
1735             } else {
1736                43
1737             };",
1738            "foo.rs",
1739            |metric| {
1740                // Spaces: 1
1741                insta::assert_json_snapshot!(
1742                    metric.loc,
1743                    @r###"
1744                    {
1745                      "sloc": 6.0,
1746                      "ploc": 6.0,
1747                      "lloc": 3.0,
1748                      "cloc": 0.0,
1749                      "blank": 0.0,
1750                      "sloc_average": 6.0,
1751                      "ploc_average": 6.0,
1752                      "lloc_average": 3.0,
1753                      "cloc_average": 0.0,
1754                      "blank_average": 0.0,
1755                      "sloc_min": 6.0,
1756                      "sloc_max": 6.0,
1757                      "cloc_min": 0.0,
1758                      "cloc_max": 0.0,
1759                      "ploc_min": 6.0,
1760                      "ploc_max": 6.0,
1761                      "lloc_min": 3.0,
1762                      "lloc_max": 3.0,
1763                      "blank_min": 0.0,
1764                      "blank_max": 0.0
1765                    }"###
1766                );
1767            },
1768        );
1769    }
1770
1771    #[test]
1772    fn c_lloc() {
1773        check_metrics::<CppParser>(
1774            "for (;;)
1775                break;",
1776            "foo.c",
1777            |metric| {
1778                // Spaces: 1
1779                insta::assert_json_snapshot!(
1780                    metric.loc,
1781                    @r###"
1782                    {
1783                      "sloc": 2.0,
1784                      "ploc": 2.0,
1785                      "lloc": 2.0,
1786                      "cloc": 0.0,
1787                      "blank": 0.0,
1788                      "sloc_average": 2.0,
1789                      "ploc_average": 2.0,
1790                      "lloc_average": 2.0,
1791                      "cloc_average": 0.0,
1792                      "blank_average": 0.0,
1793                      "sloc_min": 2.0,
1794                      "sloc_max": 2.0,
1795                      "cloc_min": 0.0,
1796                      "cloc_max": 0.0,
1797                      "ploc_min": 2.0,
1798                      "ploc_max": 2.0,
1799                      "lloc_min": 2.0,
1800                      "lloc_max": 2.0,
1801                      "blank_min": 0.0,
1802                      "blank_max": 0.0
1803                    }"###
1804                );
1805            },
1806        );
1807    }
1808
1809    #[test]
1810    fn cpp_lloc() {
1811        check_metrics::<CppParser>(
1812            "nsTArray<xpcGCCallback> callbacks(extraGCCallbacks.Clone());
1813             for (uint32_t i = 0; i < callbacks.Length(); ++i) {
1814                 callbacks[i](status);
1815             }",
1816            "foo.cpp",
1817            |metric| {
1818                // Spaces: 1
1819                // lloc: nsTArray, for, callbacks
1820                insta::assert_json_snapshot!(
1821                    metric.loc,
1822                    @r###"
1823                    {
1824                      "sloc": 4.0,
1825                      "ploc": 4.0,
1826                      "lloc": 3.0,
1827                      "cloc": 0.0,
1828                      "blank": 0.0,
1829                      "sloc_average": 4.0,
1830                      "ploc_average": 4.0,
1831                      "lloc_average": 3.0,
1832                      "cloc_average": 0.0,
1833                      "blank_average": 0.0,
1834                      "sloc_min": 4.0,
1835                      "sloc_max": 4.0,
1836                      "cloc_min": 0.0,
1837                      "cloc_max": 0.0,
1838                      "ploc_min": 4.0,
1839                      "ploc_max": 4.0,
1840                      "lloc_min": 3.0,
1841                      "lloc_max": 3.0,
1842                      "blank_min": 0.0,
1843                      "blank_max": 0.0
1844                    }"###
1845                );
1846            },
1847        );
1848    }
1849
1850    #[test]
1851    fn cpp_return_lloc() {
1852        check_metrics::<CppParser>(
1853            "uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
1854             return RgbaColor(pixel_data) == blank_pixel_;",
1855            "foo.cpp",
1856            |metric| {
1857                // Spaces: 1
1858                // lloc: pixel_data, return
1859                insta::assert_json_snapshot!(
1860                    metric.loc,
1861                    @r###"
1862                    {
1863                      "sloc": 2.0,
1864                      "ploc": 2.0,
1865                      "lloc": 2.0,
1866                      "cloc": 0.0,
1867                      "blank": 0.0,
1868                      "sloc_average": 2.0,
1869                      "ploc_average": 2.0,
1870                      "lloc_average": 2.0,
1871                      "cloc_average": 0.0,
1872                      "blank_average": 0.0,
1873                      "sloc_min": 2.0,
1874                      "sloc_max": 2.0,
1875                      "cloc_min": 0.0,
1876                      "cloc_max": 0.0,
1877                      "ploc_min": 2.0,
1878                      "ploc_max": 2.0,
1879                      "lloc_min": 2.0,
1880                      "lloc_max": 2.0,
1881                      "blank_min": 0.0,
1882                      "blank_max": 0.0
1883                    }"###
1884                );
1885            },
1886        );
1887    }
1888
1889    #[test]
1890    fn cpp_for_lloc() {
1891        check_metrics::<CppParser>(
1892            "for (; start != end; ++start) {
1893                 const unsigned char idx = *start;
1894                 if (idx > 127 || !kValidTokenMap[idx]) return false;
1895             }",
1896            "foo.cpp",
1897            |metric| {
1898                // Spaces: 1
1899                // lloc: for, idx, if, return
1900                insta::assert_json_snapshot!(
1901                    metric.loc,
1902                    @r###"
1903                    {
1904                      "sloc": 4.0,
1905                      "ploc": 4.0,
1906                      "lloc": 4.0,
1907                      "cloc": 0.0,
1908                      "blank": 0.0,
1909                      "sloc_average": 4.0,
1910                      "ploc_average": 4.0,
1911                      "lloc_average": 4.0,
1912                      "cloc_average": 0.0,
1913                      "blank_average": 0.0,
1914                      "sloc_min": 4.0,
1915                      "sloc_max": 4.0,
1916                      "cloc_min": 0.0,
1917                      "cloc_max": 0.0,
1918                      "ploc_min": 4.0,
1919                      "ploc_max": 4.0,
1920                      "lloc_min": 4.0,
1921                      "lloc_max": 4.0,
1922                      "blank_min": 0.0,
1923                      "blank_max": 0.0
1924                    }"###
1925                );
1926            },
1927        );
1928    }
1929
1930    #[test]
1931    fn cpp_while_lloc() {
1932        check_metrics::<CppParser>(
1933            "while (sHeapAtoms) {
1934                 HttpHeapAtom* next = sHeapAtoms->next;
1935                 free(sHeapAtoms);
1936            }",
1937            "foo.cpp",
1938            |metric| {
1939                // Spaces: 1
1940                // lloc: while, next, free
1941                insta::assert_json_snapshot!(
1942                    metric.loc,
1943                    @r###"
1944                    {
1945                      "sloc": 4.0,
1946                      "ploc": 4.0,
1947                      "lloc": 3.0,
1948                      "cloc": 0.0,
1949                      "blank": 0.0,
1950                      "sloc_average": 4.0,
1951                      "ploc_average": 4.0,
1952                      "lloc_average": 3.0,
1953                      "cloc_average": 0.0,
1954                      "blank_average": 0.0,
1955                      "sloc_min": 4.0,
1956                      "sloc_max": 4.0,
1957                      "cloc_min": 0.0,
1958                      "cloc_max": 0.0,
1959                      "ploc_min": 4.0,
1960                      "ploc_max": 4.0,
1961                      "lloc_min": 3.0,
1962                      "lloc_max": 3.0,
1963                      "blank_min": 0.0,
1964                      "blank_max": 0.0
1965                    }"###
1966                );
1967            },
1968        );
1969    }
1970
1971    #[test]
1972    fn python_string_on_new_line() {
1973        // More lines of the same instruction were counted as blank lines
1974        check_metrics::<PythonParser>(
1975            "capabilities[\"goog:chromeOptions\"][\"androidPackage\"] = \\
1976                \"org.chromium.weblayer.shell\"",
1977            "foo.py",
1978            |metric| {
1979                // Spaces: 1
1980                insta::assert_json_snapshot!(
1981                    metric.loc,
1982                    @r###"
1983                    {
1984                      "sloc": 2.0,
1985                      "ploc": 2.0,
1986                      "lloc": 1.0,
1987                      "cloc": 0.0,
1988                      "blank": 0.0,
1989                      "sloc_average": 2.0,
1990                      "ploc_average": 2.0,
1991                      "lloc_average": 1.0,
1992                      "cloc_average": 0.0,
1993                      "blank_average": 0.0,
1994                      "sloc_min": 2.0,
1995                      "sloc_max": 2.0,
1996                      "cloc_min": 0.0,
1997                      "cloc_max": 0.0,
1998                      "ploc_min": 2.0,
1999                      "ploc_max": 2.0,
2000                      "lloc_min": 1.0,
2001                      "lloc_max": 1.0,
2002                      "blank_min": 0.0,
2003                      "blank_max": 0.0
2004                    }"###
2005                );
2006            },
2007        );
2008    }
2009
2010    #[test]
2011    fn rust_no_field_expression_lloc() {
2012        check_metrics::<RustParser>(
2013            "struct Foo {
2014                field: usize,
2015             }
2016             let foo = Foo { 42 };
2017             foo.field;",
2018            "foo.rs",
2019            |metric| {
2020                // Spaces: 1
2021                insta::assert_json_snapshot!(
2022                    metric.loc,
2023                    @r###"
2024                    {
2025                      "sloc": 5.0,
2026                      "ploc": 5.0,
2027                      "lloc": 2.0,
2028                      "cloc": 0.0,
2029                      "blank": 0.0,
2030                      "sloc_average": 5.0,
2031                      "ploc_average": 5.0,
2032                      "lloc_average": 2.0,
2033                      "cloc_average": 0.0,
2034                      "blank_average": 0.0,
2035                      "sloc_min": 5.0,
2036                      "sloc_max": 5.0,
2037                      "cloc_min": 0.0,
2038                      "cloc_max": 0.0,
2039                      "ploc_min": 5.0,
2040                      "ploc_max": 5.0,
2041                      "lloc_min": 2.0,
2042                      "lloc_max": 2.0,
2043                      "blank_min": 0.0,
2044                      "blank_max": 0.0
2045                    }"###
2046                );
2047            },
2048        );
2049    }
2050
2051    #[test]
2052    fn rust_no_parenthesized_expression_lloc() {
2053        check_metrics::<RustParser>("let a = (42 + 0);", "foo.rs", |metric| {
2054            // Spaces: 1
2055            insta::assert_json_snapshot!(
2056                metric.loc,
2057                @r###"
2058                    {
2059                      "sloc": 1.0,
2060                      "ploc": 1.0,
2061                      "lloc": 1.0,
2062                      "cloc": 0.0,
2063                      "blank": 0.0,
2064                      "sloc_average": 1.0,
2065                      "ploc_average": 1.0,
2066                      "lloc_average": 1.0,
2067                      "cloc_average": 0.0,
2068                      "blank_average": 0.0,
2069                      "sloc_min": 1.0,
2070                      "sloc_max": 1.0,
2071                      "cloc_min": 0.0,
2072                      "cloc_max": 0.0,
2073                      "ploc_min": 1.0,
2074                      "ploc_max": 1.0,
2075                      "lloc_min": 1.0,
2076                      "lloc_max": 1.0,
2077                      "blank_min": 0.0,
2078                      "blank_max": 0.0
2079                    }"###
2080            );
2081        });
2082    }
2083
2084    #[test]
2085    fn rust_no_array_expression_lloc() {
2086        check_metrics::<RustParser>("let a = [0; 42];", "foo.rs", |metric| {
2087            // Spaces: 1
2088            insta::assert_json_snapshot!(
2089                metric.loc,
2090                @r###"
2091                    {
2092                      "sloc": 1.0,
2093                      "ploc": 1.0,
2094                      "lloc": 1.0,
2095                      "cloc": 0.0,
2096                      "blank": 0.0,
2097                      "sloc_average": 1.0,
2098                      "ploc_average": 1.0,
2099                      "lloc_average": 1.0,
2100                      "cloc_average": 0.0,
2101                      "blank_average": 0.0,
2102                      "sloc_min": 1.0,
2103                      "sloc_max": 1.0,
2104                      "cloc_min": 0.0,
2105                      "cloc_max": 0.0,
2106                      "ploc_min": 1.0,
2107                      "ploc_max": 1.0,
2108                      "lloc_min": 1.0,
2109                      "lloc_max": 1.0,
2110                      "blank_min": 0.0,
2111                      "blank_max": 0.0
2112                    }"###
2113            );
2114        });
2115    }
2116
2117    #[test]
2118    fn rust_no_tuple_expression_lloc() {
2119        check_metrics::<RustParser>("let a = (0, 42);", "foo.rs", |metric| {
2120            // Spaces: 1
2121            insta::assert_json_snapshot!(
2122                metric.loc,
2123                @r###"
2124                    {
2125                      "sloc": 1.0,
2126                      "ploc": 1.0,
2127                      "lloc": 1.0,
2128                      "cloc": 0.0,
2129                      "blank": 0.0,
2130                      "sloc_average": 1.0,
2131                      "ploc_average": 1.0,
2132                      "lloc_average": 1.0,
2133                      "cloc_average": 0.0,
2134                      "blank_average": 0.0,
2135                      "sloc_min": 1.0,
2136                      "sloc_max": 1.0,
2137                      "cloc_min": 0.0,
2138                      "cloc_max": 0.0,
2139                      "ploc_min": 1.0,
2140                      "ploc_max": 1.0,
2141                      "lloc_min": 1.0,
2142                      "lloc_max": 1.0,
2143                      "blank_min": 0.0,
2144                      "blank_max": 0.0
2145                    }"###
2146            );
2147        });
2148    }
2149
2150    #[test]
2151    fn rust_no_unit_expression_lloc() {
2152        check_metrics::<RustParser>("let a = ();", "foo.rs", |metric| {
2153            // Spaces: 1
2154            insta::assert_json_snapshot!(
2155                metric.loc,
2156                @r###"
2157                    {
2158                      "sloc": 1.0,
2159                      "ploc": 1.0,
2160                      "lloc": 1.0,
2161                      "cloc": 0.0,
2162                      "blank": 0.0,
2163                      "sloc_average": 1.0,
2164                      "ploc_average": 1.0,
2165                      "lloc_average": 1.0,
2166                      "cloc_average": 0.0,
2167                      "blank_average": 0.0,
2168                      "sloc_min": 1.0,
2169                      "sloc_max": 1.0,
2170                      "cloc_min": 0.0,
2171                      "cloc_max": 0.0,
2172                      "ploc_min": 1.0,
2173                      "ploc_max": 1.0,
2174                      "lloc_min": 1.0,
2175                      "lloc_max": 1.0,
2176                      "blank_min": 0.0,
2177                      "blank_max": 0.0
2178                    }"###
2179            );
2180        });
2181    }
2182
2183    #[test]
2184    fn rust_call_function_lloc() {
2185        check_metrics::<RustParser>(
2186            "let a = foo(); // +1
2187             foo(); // +1
2188             k!(foo()); // +1",
2189            "foo.rs",
2190            |metric| {
2191                // Spaces: 1
2192                insta::assert_json_snapshot!(
2193                    metric.loc,
2194                    @r###"
2195                    {
2196                      "sloc": 3.0,
2197                      "ploc": 3.0,
2198                      "lloc": 3.0,
2199                      "cloc": 3.0,
2200                      "blank": 0.0,
2201                      "sloc_average": 3.0,
2202                      "ploc_average": 3.0,
2203                      "lloc_average": 3.0,
2204                      "cloc_average": 3.0,
2205                      "blank_average": 0.0,
2206                      "sloc_min": 3.0,
2207                      "sloc_max": 3.0,
2208                      "cloc_min": 3.0,
2209                      "cloc_max": 3.0,
2210                      "ploc_min": 3.0,
2211                      "ploc_max": 3.0,
2212                      "lloc_min": 3.0,
2213                      "lloc_max": 3.0,
2214                      "blank_min": 0.0,
2215                      "blank_max": 0.0
2216                    }"###
2217                );
2218            },
2219        );
2220    }
2221
2222    #[test]
2223    fn rust_macro_invocation_lloc() {
2224        check_metrics::<RustParser>(
2225            "let a = foo!(); // +1
2226             foo!(); // +1
2227             k(foo!()); // +1",
2228            "foo.rs",
2229            |metric| {
2230                // Spaces: 1
2231                insta::assert_json_snapshot!(
2232                    metric.loc,
2233                    @r###"
2234                    {
2235                      "sloc": 3.0,
2236                      "ploc": 3.0,
2237                      "lloc": 3.0,
2238                      "cloc": 3.0,
2239                      "blank": 0.0,
2240                      "sloc_average": 3.0,
2241                      "ploc_average": 3.0,
2242                      "lloc_average": 3.0,
2243                      "cloc_average": 3.0,
2244                      "blank_average": 0.0,
2245                      "sloc_min": 3.0,
2246                      "sloc_max": 3.0,
2247                      "cloc_min": 3.0,
2248                      "cloc_max": 3.0,
2249                      "ploc_min": 3.0,
2250                      "ploc_max": 3.0,
2251                      "lloc_min": 3.0,
2252                      "lloc_max": 3.0,
2253                      "blank_min": 0.0,
2254                      "blank_max": 0.0
2255                    }"###
2256                );
2257            },
2258        );
2259    }
2260
2261    #[test]
2262    fn rust_function_in_loop_lloc() {
2263        check_metrics::<RustParser>(
2264            "for (a, b) in c.iter().enumerate() {} // +1
2265             while (a, b) in c.iter().enumerate() {} // +1
2266             while let Some(a) = c.strip_prefix(\"hi\") {} // +1",
2267            "foo.rs",
2268            |metric| {
2269                // Spaces: 1
2270                insta::assert_json_snapshot!(
2271                    metric.loc,
2272                    @r###"
2273                    {
2274                      "sloc": 3.0,
2275                      "ploc": 3.0,
2276                      "lloc": 3.0,
2277                      "cloc": 3.0,
2278                      "blank": 0.0,
2279                      "sloc_average": 3.0,
2280                      "ploc_average": 3.0,
2281                      "lloc_average": 3.0,
2282                      "cloc_average": 3.0,
2283                      "blank_average": 0.0,
2284                      "sloc_min": 3.0,
2285                      "sloc_max": 3.0,
2286                      "cloc_min": 3.0,
2287                      "cloc_max": 3.0,
2288                      "ploc_min": 3.0,
2289                      "ploc_max": 3.0,
2290                      "lloc_min": 3.0,
2291                      "lloc_max": 3.0,
2292                      "blank_min": 0.0,
2293                      "blank_max": 0.0
2294                    }"###
2295                );
2296            },
2297        );
2298    }
2299
2300    #[test]
2301    fn rust_function_in_if_lloc() {
2302        check_metrics::<RustParser>(
2303            "if foo() {} // +1
2304             if let Some(a) = foo() {} // +1",
2305            "foo.rs",
2306            |metric| {
2307                // Spaces: 1
2308                insta::assert_json_snapshot!(
2309                    metric.loc,
2310                    @r###"
2311                    {
2312                      "sloc": 2.0,
2313                      "ploc": 2.0,
2314                      "lloc": 2.0,
2315                      "cloc": 2.0,
2316                      "blank": 0.0,
2317                      "sloc_average": 2.0,
2318                      "ploc_average": 2.0,
2319                      "lloc_average": 2.0,
2320                      "cloc_average": 2.0,
2321                      "blank_average": 0.0,
2322                      "sloc_min": 2.0,
2323                      "sloc_max": 2.0,
2324                      "cloc_min": 2.0,
2325                      "cloc_max": 2.0,
2326                      "ploc_min": 2.0,
2327                      "ploc_max": 2.0,
2328                      "lloc_min": 2.0,
2329                      "lloc_max": 2.0,
2330                      "blank_min": 0.0,
2331                      "blank_max": 0.0
2332                    }"###
2333                );
2334            },
2335        );
2336    }
2337
2338    #[test]
2339    fn rust_function_in_return_lloc() {
2340        check_metrics::<RustParser>(
2341            "return foo();
2342             await foo();",
2343            "foo.rs",
2344            |metric| {
2345                // Spaces: 1
2346                insta::assert_json_snapshot!(
2347                    metric.loc,
2348                    @r###"
2349                    {
2350                      "sloc": 2.0,
2351                      "ploc": 2.0,
2352                      "lloc": 2.0,
2353                      "cloc": 0.0,
2354                      "blank": 0.0,
2355                      "sloc_average": 2.0,
2356                      "ploc_average": 2.0,
2357                      "lloc_average": 2.0,
2358                      "cloc_average": 0.0,
2359                      "blank_average": 0.0,
2360                      "sloc_min": 2.0,
2361                      "sloc_max": 2.0,
2362                      "cloc_min": 0.0,
2363                      "cloc_max": 0.0,
2364                      "ploc_min": 2.0,
2365                      "ploc_max": 2.0,
2366                      "lloc_min": 2.0,
2367                      "lloc_max": 2.0,
2368                      "blank_min": 0.0,
2369                      "blank_max": 0.0
2370                    }"###
2371                );
2372            },
2373        );
2374    }
2375
2376    #[test]
2377    fn rust_closure_expression_lloc() {
2378        check_metrics::<RustParser>(
2379            "let a = |i: i32| -> i32 { i + 1 }; // +1
2380             a(42); // +1
2381             k(b.iter().map(|n| n.parse.ok().unwrap_or(42))); // +1",
2382            "foo.rs",
2383            |metric| {
2384                // Spaces: 3
2385                insta::assert_json_snapshot!(
2386                    metric.loc,
2387                    @r###"
2388                    {
2389                      "sloc": 3.0,
2390                      "ploc": 3.0,
2391                      "lloc": 3.0,
2392                      "cloc": 3.0,
2393                      "blank": 0.0,
2394                      "sloc_average": 1.0,
2395                      "ploc_average": 1.0,
2396                      "lloc_average": 1.0,
2397                      "cloc_average": 1.0,
2398                      "blank_average": 0.0,
2399                      "sloc_min": 1.0,
2400                      "sloc_max": 1.0,
2401                      "cloc_min": 0.0,
2402                      "cloc_max": 0.0,
2403                      "ploc_min": 1.0,
2404                      "ploc_max": 1.0,
2405                      "lloc_min": 0.0,
2406                      "lloc_max": 0.0,
2407                      "blank_min": 0.0,
2408                      "blank_max": 0.0
2409                    }"###
2410                );
2411            },
2412        );
2413    }
2414
2415    #[test]
2416    fn python_general_loc() {
2417        check_metrics::<PythonParser>(
2418            "def func(a,
2419                      b,
2420                      c):
2421                 print(a)
2422                 print(b)
2423                 print(c)",
2424            "foo.py",
2425            |metric| {
2426                // Spaces: 2
2427                insta::assert_json_snapshot!(
2428                    metric.loc,
2429                    @r###"
2430                    {
2431                      "sloc": 6.0,
2432                      "ploc": 6.0,
2433                      "lloc": 3.0,
2434                      "cloc": 0.0,
2435                      "blank": 0.0,
2436                      "sloc_average": 3.0,
2437                      "ploc_average": 3.0,
2438                      "lloc_average": 1.5,
2439                      "cloc_average": 0.0,
2440                      "blank_average": 0.0,
2441                      "sloc_min": 6.0,
2442                      "sloc_max": 6.0,
2443                      "cloc_min": 0.0,
2444                      "cloc_max": 0.0,
2445                      "ploc_min": 6.0,
2446                      "ploc_max": 6.0,
2447                      "lloc_min": 3.0,
2448                      "lloc_max": 3.0,
2449                      "blank_min": 0.0,
2450                      "blank_max": 0.0
2451                    }"###
2452                );
2453            },
2454        );
2455    }
2456
2457    #[test]
2458    fn python_real_loc() {
2459        check_metrics::<PythonParser>(
2460            "def web_socket_transfer_data(request):
2461                while True:
2462                    line = request.ws_stream.receive_message()
2463                    if line is None:
2464                        return
2465                    code, reason = line.split(' ', 1)
2466                    if code is None or reason is None:
2467                        return
2468                    request.ws_stream.close_connection(int(code), reason)
2469                    # close_connection() initiates closing handshake. It validates code
2470                    # and reason. If you want to send a broken close frame for a test,
2471                    # following code will be useful.
2472                    # > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
2473                    # > request.connection.write(stream.create_close_frame(data))
2474                    # > # Suppress to re-respond client responding close frame.
2475                    # > raise Exception(\"customized server initiated closing handshake\")",
2476            "foo.py",
2477            |metric| {
2478                // Spaces: 2
2479                insta::assert_json_snapshot!(
2480                    metric.loc,
2481                    @r###"
2482                    {
2483                      "sloc": 16.0,
2484                      "ploc": 9.0,
2485                      "lloc": 8.0,
2486                      "cloc": 7.0,
2487                      "blank": 0.0,
2488                      "sloc_average": 8.0,
2489                      "ploc_average": 4.5,
2490                      "lloc_average": 4.0,
2491                      "cloc_average": 3.5,
2492                      "blank_average": 0.0,
2493                      "sloc_min": 16.0,
2494                      "sloc_max": 16.0,
2495                      "cloc_min": 7.0,
2496                      "cloc_max": 7.0,
2497                      "ploc_min": 9.0,
2498                      "ploc_max": 9.0,
2499                      "lloc_min": 8.0,
2500                      "lloc_max": 8.0,
2501                      "blank_min": 0.0,
2502                      "blank_max": 0.0
2503                    }"###
2504                );
2505            },
2506        );
2507    }
2508
2509    #[test]
2510    fn javascript_real_loc() {
2511        check_metrics::<JavascriptParser>(
2512            "assert.throws(Test262Error, function() {
2513               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
2514                 return;
2515               }
2516             });",
2517            "foo.js",
2518            |metric| {
2519                // Spaces: 2
2520                insta::assert_json_snapshot!(
2521                    metric.loc,
2522                    @r###"
2523                    {
2524                      "sloc": 5.0,
2525                      "ploc": 5.0,
2526                      "lloc": 6.0,
2527                      "cloc": 0.0,
2528                      "blank": 0.0,
2529                      "sloc_average": 2.5,
2530                      "ploc_average": 2.5,
2531                      "lloc_average": 3.0,
2532                      "cloc_average": 0.0,
2533                      "blank_average": 0.0,
2534                      "sloc_min": 5.0,
2535                      "sloc_max": 5.0,
2536                      "cloc_min": 0.0,
2537                      "cloc_max": 0.0,
2538                      "ploc_min": 5.0,
2539                      "ploc_max": 5.0,
2540                      "lloc_min": 5.0,
2541                      "lloc_max": 5.0,
2542                      "blank_min": 0.0,
2543                      "blank_max": 0.0
2544                    }"###
2545                );
2546            },
2547        );
2548    }
2549
2550    #[test]
2551    fn mozjs_real_loc() {
2552        check_metrics::<JavascriptParser>(
2553            "assert.throws(Test262Error, function() {
2554               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
2555                 return;
2556               }
2557             });",
2558            "foo.js",
2559            |metric| {
2560                // Spaces: 2
2561                insta::assert_json_snapshot!(
2562                    metric.loc,
2563                    @r###"
2564                    {
2565                      "sloc": 5.0,
2566                      "ploc": 5.0,
2567                      "lloc": 6.0,
2568                      "cloc": 0.0,
2569                      "blank": 0.0,
2570                      "sloc_average": 2.5,
2571                      "ploc_average": 2.5,
2572                      "lloc_average": 3.0,
2573                      "cloc_average": 0.0,
2574                      "blank_average": 0.0,
2575                      "sloc_min": 5.0,
2576                      "sloc_max": 5.0,
2577                      "cloc_min": 0.0,
2578                      "cloc_max": 0.0,
2579                      "ploc_min": 5.0,
2580                      "ploc_max": 5.0,
2581                      "lloc_min": 5.0,
2582                      "lloc_max": 5.0,
2583                      "blank_min": 0.0,
2584                      "blank_max": 0.0
2585                    }"###
2586                );
2587            },
2588        );
2589    }
2590
2591    #[test]
2592    fn cpp_namespace_loc() {
2593        check_metrics::<CppParser>(
2594            "namespace mozilla::dom::quota {} // namespace mozilla::dom::quota",
2595            "foo.cpp",
2596            |metric| {
2597                // Spaces: 2
2598                insta::assert_json_snapshot!(
2599                    metric.loc,
2600                    @r###"
2601                    {
2602                      "sloc": 1.0,
2603                      "ploc": 1.0,
2604                      "lloc": 0.0,
2605                      "cloc": 1.0,
2606                      "blank": 0.0,
2607                      "sloc_average": 0.5,
2608                      "ploc_average": 0.5,
2609                      "lloc_average": 0.0,
2610                      "cloc_average": 0.5,
2611                      "blank_average": 0.0,
2612                      "sloc_min": 1.0,
2613                      "sloc_max": 1.0,
2614                      "cloc_min": 0.0,
2615                      "cloc_max": 0.0,
2616                      "ploc_min": 1.0,
2617                      "ploc_max": 1.0,
2618                      "lloc_min": 0.0,
2619                      "lloc_max": 0.0,
2620                      "blank_min": 0.0,
2621                      "blank_max": 0.0
2622                    }"###
2623                );
2624            },
2625        );
2626    }
2627
2628    #[test]
2629    fn java_comments() {
2630        check_metrics::<JavaParser>(
2631            "for (int i = 0; i < 100; i++) { \
2632               // Print hello
2633               System.out.println(\"hello\"); \
2634               // Print world
2635               System.out.println(\"hello\"); \
2636             }",
2637            "foo.java",
2638            |metric| {
2639                // Spaces: 1
2640                insta::assert_json_snapshot!(
2641                    metric.loc,
2642                    @r###"
2643                    {
2644                      "sloc": 3.0,
2645                      "ploc": 3.0,
2646                      "lloc": 3.0,
2647                      "cloc": 2.0,
2648                      "blank": 0.0,
2649                      "sloc_average": 3.0,
2650                      "ploc_average": 3.0,
2651                      "lloc_average": 3.0,
2652                      "cloc_average": 2.0,
2653                      "blank_average": 0.0,
2654                      "sloc_min": 3.0,
2655                      "sloc_max": 3.0,
2656                      "cloc_min": 2.0,
2657                      "cloc_max": 2.0,
2658                      "ploc_min": 3.0,
2659                      "ploc_max": 3.0,
2660                      "lloc_min": 3.0,
2661                      "lloc_max": 3.0,
2662                      "blank_min": 0.0,
2663                      "blank_max": 0.0
2664                    }"###
2665                );
2666            },
2667        );
2668    }
2669
2670    #[test]
2671    fn java_blank() {
2672        check_metrics::<JavaParser>(
2673            "int x = 1;
2674
2675
2676            int y = 2;",
2677            "foo.java",
2678            |metric| {
2679                // Spaces: 1
2680                insta::assert_json_snapshot!(
2681                    metric.loc,
2682                    @r###"
2683                    {
2684                      "sloc": 4.0,
2685                      "ploc": 2.0,
2686                      "lloc": 2.0,
2687                      "cloc": 0.0,
2688                      "blank": 2.0,
2689                      "sloc_average": 4.0,
2690                      "ploc_average": 2.0,
2691                      "lloc_average": 2.0,
2692                      "cloc_average": 0.0,
2693                      "blank_average": 2.0,
2694                      "sloc_min": 4.0,
2695                      "sloc_max": 4.0,
2696                      "cloc_min": 0.0,
2697                      "cloc_max": 0.0,
2698                      "ploc_min": 2.0,
2699                      "ploc_max": 2.0,
2700                      "lloc_min": 2.0,
2701                      "lloc_max": 2.0,
2702                      "blank_min": 2.0,
2703                      "blank_max": 2.0
2704                    }"###
2705                );
2706            },
2707        );
2708    }
2709
2710    #[test]
2711    fn java_sloc() {
2712        check_metrics::<JavaParser>(
2713            "for (int i = 0; i < 100; i++) {
2714               System.out.println(i);
2715             }",
2716            "foo.java",
2717            |metric| {
2718                // Spaces: 1
2719                insta::assert_json_snapshot!(
2720                    metric.loc,
2721                    @r###"
2722                    {
2723                      "sloc": 3.0,
2724                      "ploc": 3.0,
2725                      "lloc": 2.0,
2726                      "cloc": 0.0,
2727                      "blank": 0.0,
2728                      "sloc_average": 3.0,
2729                      "ploc_average": 3.0,
2730                      "lloc_average": 2.0,
2731                      "cloc_average": 0.0,
2732                      "blank_average": 0.0,
2733                      "sloc_min": 3.0,
2734                      "sloc_max": 3.0,
2735                      "cloc_min": 0.0,
2736                      "cloc_max": 0.0,
2737                      "ploc_min": 3.0,
2738                      "ploc_max": 3.0,
2739                      "lloc_min": 2.0,
2740                      "lloc_max": 2.0,
2741                      "blank_min": 0.0,
2742                      "blank_max": 0.0
2743                    }"###
2744                );
2745            },
2746        );
2747    }
2748
2749    #[test]
2750    fn java_module_sloc() {
2751        check_metrics::<JavaParser>(
2752            "module helloworld{
2753              exports com.test;
2754            }",
2755            "foo.java",
2756            |metric| {
2757                // Spaces: 1
2758                insta::assert_json_snapshot!(
2759                    metric.loc,
2760                    @r###"
2761                    {
2762                      "sloc": 3.0,
2763                      "ploc": 3.0,
2764                      "lloc": 0.0,
2765                      "cloc": 0.0,
2766                      "blank": 0.0,
2767                      "sloc_average": 3.0,
2768                      "ploc_average": 3.0,
2769                      "lloc_average": 0.0,
2770                      "cloc_average": 0.0,
2771                      "blank_average": 0.0,
2772                      "sloc_min": 3.0,
2773                      "sloc_max": 3.0,
2774                      "cloc_min": 0.0,
2775                      "cloc_max": 0.0,
2776                      "ploc_min": 3.0,
2777                      "ploc_max": 3.0,
2778                      "lloc_min": 0.0,
2779                      "lloc_max": 0.0,
2780                      "blank_min": 0.0,
2781                      "blank_max": 0.0
2782                    }"###
2783                );
2784            },
2785        );
2786    }
2787
2788    #[test]
2789    fn java_single_ploc() {
2790        check_metrics::<JavaParser>("int x = 1;", "foo.java", |metric| {
2791            // Spaces: 1
2792            insta::assert_json_snapshot!(
2793                metric.loc,
2794                @r###"
2795                    {
2796                      "sloc": 1.0,
2797                      "ploc": 1.0,
2798                      "lloc": 1.0,
2799                      "cloc": 0.0,
2800                      "blank": 0.0,
2801                      "sloc_average": 1.0,
2802                      "ploc_average": 1.0,
2803                      "lloc_average": 1.0,
2804                      "cloc_average": 0.0,
2805                      "blank_average": 0.0,
2806                      "sloc_min": 1.0,
2807                      "sloc_max": 1.0,
2808                      "cloc_min": 0.0,
2809                      "cloc_max": 0.0,
2810                      "ploc_min": 1.0,
2811                      "ploc_max": 1.0,
2812                      "lloc_min": 1.0,
2813                      "lloc_max": 1.0,
2814                      "blank_min": 0.0,
2815                      "blank_max": 0.0
2816                    }"###
2817            );
2818        });
2819    }
2820
2821    #[test]
2822    fn java_simple_ploc() {
2823        check_metrics::<JavaParser>(
2824            "for (int i = 0; i < 100; i = i++) {
2825               System.out.println(i);
2826             }",
2827            "foo.java",
2828            |metric| {
2829                // Spaces: 1
2830                insta::assert_json_snapshot!(
2831                    metric.loc,
2832                    @r###"
2833                    {
2834                      "sloc": 3.0,
2835                      "ploc": 3.0,
2836                      "lloc": 2.0,
2837                      "cloc": 0.0,
2838                      "blank": 0.0,
2839                      "sloc_average": 3.0,
2840                      "ploc_average": 3.0,
2841                      "lloc_average": 2.0,
2842                      "cloc_average": 0.0,
2843                      "blank_average": 0.0,
2844                      "sloc_min": 3.0,
2845                      "sloc_max": 3.0,
2846                      "cloc_min": 0.0,
2847                      "cloc_max": 0.0,
2848                      "ploc_min": 3.0,
2849                      "ploc_max": 3.0,
2850                      "lloc_min": 2.0,
2851                      "lloc_max": 2.0,
2852                      "blank_min": 0.0,
2853                      "blank_max": 0.0
2854                    }"###
2855                );
2856            },
2857        );
2858    }
2859
2860    #[test]
2861    fn java_multi_ploc() {
2862        check_metrics::<JavaParser>(
2863            "int x = 1;
2864            for (int i = 0; i < 100; i++) {
2865               System.out.println(i);
2866             }",
2867            "foo.java",
2868            |metric| {
2869                // Spaces: 1
2870                insta::assert_json_snapshot!(
2871                    metric.loc,
2872                    @r###"
2873                    {
2874                      "sloc": 4.0,
2875                      "ploc": 4.0,
2876                      "lloc": 3.0,
2877                      "cloc": 0.0,
2878                      "blank": 0.0,
2879                      "sloc_average": 4.0,
2880                      "ploc_average": 4.0,
2881                      "lloc_average": 3.0,
2882                      "cloc_average": 0.0,
2883                      "blank_average": 0.0,
2884                      "sloc_min": 4.0,
2885                      "sloc_max": 4.0,
2886                      "cloc_min": 0.0,
2887                      "cloc_max": 0.0,
2888                      "ploc_min": 4.0,
2889                      "ploc_max": 4.0,
2890                      "lloc_min": 3.0,
2891                      "lloc_max": 3.0,
2892                      "blank_min": 0.0,
2893                      "blank_max": 0.0
2894                    }"###
2895                );
2896            },
2897        );
2898    }
2899
2900    #[test]
2901    fn java_single_statement_lloc() {
2902        check_metrics::<JavaParser>("int max = 10;", "foo.java", |metric| {
2903            // Spaces: 1
2904            insta::assert_json_snapshot!(
2905                metric.loc,
2906                @r###"
2907                    {
2908                      "sloc": 1.0,
2909                      "ploc": 1.0,
2910                      "lloc": 1.0,
2911                      "cloc": 0.0,
2912                      "blank": 0.0,
2913                      "sloc_average": 1.0,
2914                      "ploc_average": 1.0,
2915                      "lloc_average": 1.0,
2916                      "cloc_average": 0.0,
2917                      "blank_average": 0.0,
2918                      "sloc_min": 1.0,
2919                      "sloc_max": 1.0,
2920                      "cloc_min": 0.0,
2921                      "cloc_max": 0.0,
2922                      "ploc_min": 1.0,
2923                      "ploc_max": 1.0,
2924                      "lloc_min": 1.0,
2925                      "lloc_max": 1.0,
2926                      "blank_min": 0.0,
2927                      "blank_max": 0.0
2928                    }"###
2929            );
2930        });
2931    }
2932
2933    #[test]
2934    fn java_for_lloc() {
2935        check_metrics::<JavaParser>(
2936            "for (int i = 0; i < 100; i++) { // + 1
2937               System.out.println(i); // + 1
2938             }",
2939            "foo.java",
2940            |metric| {
2941                // Spaces: 1
2942                insta::assert_json_snapshot!(
2943                    metric.loc,
2944                    @r###"
2945                    {
2946                      "sloc": 3.0,
2947                      "ploc": 3.0,
2948                      "lloc": 2.0,
2949                      "cloc": 2.0,
2950                      "blank": 0.0,
2951                      "sloc_average": 3.0,
2952                      "ploc_average": 3.0,
2953                      "lloc_average": 2.0,
2954                      "cloc_average": 2.0,
2955                      "blank_average": 0.0,
2956                      "sloc_min": 3.0,
2957                      "sloc_max": 3.0,
2958                      "cloc_min": 2.0,
2959                      "cloc_max": 2.0,
2960                      "ploc_min": 3.0,
2961                      "ploc_max": 3.0,
2962                      "lloc_min": 2.0,
2963                      "lloc_max": 2.0,
2964                      "blank_min": 0.0,
2965                      "blank_max": 0.0
2966                    }"###
2967                );
2968            },
2969        );
2970    }
2971
2972    #[test]
2973    fn java_foreach_lloc() {
2974        check_metrics::<JavaParser>(
2975            "
2976            int arr[]={12,13,14,44}; // +1
2977            for (int i:arr) { // +1
2978               System.out.println(i); // +1
2979             }",
2980            "foo.java",
2981            |metric| {
2982                // Spaces: 1
2983                insta::assert_json_snapshot!(
2984                    metric.loc,
2985                    @r###"
2986                    {
2987                      "sloc": 4.0,
2988                      "ploc": 4.0,
2989                      "lloc": 3.0,
2990                      "cloc": 3.0,
2991                      "blank": 0.0,
2992                      "sloc_average": 4.0,
2993                      "ploc_average": 4.0,
2994                      "lloc_average": 3.0,
2995                      "cloc_average": 3.0,
2996                      "blank_average": 0.0,
2997                      "sloc_min": 4.0,
2998                      "sloc_max": 4.0,
2999                      "cloc_min": 3.0,
3000                      "cloc_max": 3.0,
3001                      "ploc_min": 4.0,
3002                      "ploc_max": 4.0,
3003                      "lloc_min": 3.0,
3004                      "lloc_max": 3.0,
3005                      "blank_min": 0.0,
3006                      "blank_max": 0.0
3007                    }"###
3008                );
3009            },
3010        );
3011    }
3012
3013    #[test]
3014    fn java_while_lloc() {
3015        check_metrics::<JavaParser>(
3016            "
3017            int i=0; // +1
3018            while(i < 10) { // +1
3019                i++; // +1
3020                System.out.println(i); // +1
3021             }",
3022            "foo.java",
3023            |metric| {
3024                // Spaces: 1
3025                insta::assert_json_snapshot!(
3026                    metric.loc,
3027                    @r###"
3028                    {
3029                      "sloc": 5.0,
3030                      "ploc": 5.0,
3031                      "lloc": 4.0,
3032                      "cloc": 4.0,
3033                      "blank": 0.0,
3034                      "sloc_average": 5.0,
3035                      "ploc_average": 5.0,
3036                      "lloc_average": 4.0,
3037                      "cloc_average": 4.0,
3038                      "blank_average": 0.0,
3039                      "sloc_min": 5.0,
3040                      "sloc_max": 5.0,
3041                      "cloc_min": 4.0,
3042                      "cloc_max": 4.0,
3043                      "ploc_min": 5.0,
3044                      "ploc_max": 5.0,
3045                      "lloc_min": 4.0,
3046                      "lloc_max": 4.0,
3047                      "blank_min": 0.0,
3048                      "blank_max": 0.0
3049                    }"###
3050                );
3051            },
3052        );
3053    }
3054
3055    #[test]
3056    fn java_do_while_lloc() {
3057        check_metrics::<JavaParser>(
3058            "
3059            int i=0; // +1
3060            do { // +1
3061                i++; // +1
3062                System.out.println(i); // +1
3063             } while(i < 10)",
3064            "foo.java",
3065            |metric| {
3066                // Spaces: 1
3067                insta::assert_json_snapshot!(
3068                    metric.loc,
3069                    @r###"
3070                    {
3071                      "sloc": 5.0,
3072                      "ploc": 5.0,
3073                      "lloc": 4.0,
3074                      "cloc": 4.0,
3075                      "blank": 0.0,
3076                      "sloc_average": 5.0,
3077                      "ploc_average": 5.0,
3078                      "lloc_average": 4.0,
3079                      "cloc_average": 4.0,
3080                      "blank_average": 0.0,
3081                      "sloc_min": 5.0,
3082                      "sloc_max": 5.0,
3083                      "cloc_min": 4.0,
3084                      "cloc_max": 4.0,
3085                      "ploc_min": 5.0,
3086                      "ploc_max": 5.0,
3087                      "lloc_min": 4.0,
3088                      "lloc_max": 4.0,
3089                      "blank_min": 0.0,
3090                      "blank_max": 0.0
3091                    }"###
3092                );
3093            },
3094        );
3095    }
3096
3097    #[test]
3098    fn java_switch_lloc() {
3099        check_metrics::<JavaParser>(
3100            "switch(grade) { // +1
3101                case 'A' :
3102                   System.out.println(\"Pass with distinction\"); // +1
3103                   break; // +1
3104                case 'B' :
3105                case 'C' :
3106                   System.out.println(\"Pass\"); // +1
3107                   break; // +1
3108                case 'D' :
3109                   System.out.println(\"At risk\"); // +1
3110                case 'F' :
3111                   System.out.println(\"Fail\"); // +1
3112                   break; // +1
3113                default :
3114                   System.out.println(\"Invalid grade\"); // +1
3115             }",
3116            "foo.java",
3117            |metric| {
3118                // Spaces: 1
3119                insta::assert_json_snapshot!(
3120                    metric.loc,
3121                    @r###"
3122                    {
3123                      "sloc": 16.0,
3124                      "ploc": 16.0,
3125                      "lloc": 9.0,
3126                      "cloc": 9.0,
3127                      "blank": 0.0,
3128                      "sloc_average": 16.0,
3129                      "ploc_average": 16.0,
3130                      "lloc_average": 9.0,
3131                      "cloc_average": 9.0,
3132                      "blank_average": 0.0,
3133                      "sloc_min": 16.0,
3134                      "sloc_max": 16.0,
3135                      "cloc_min": 9.0,
3136                      "cloc_max": 9.0,
3137                      "ploc_min": 16.0,
3138                      "ploc_max": 16.0,
3139                      "lloc_min": 9.0,
3140                      "lloc_max": 9.0,
3141                      "blank_min": 0.0,
3142                      "blank_max": 0.0
3143                    }"###
3144                );
3145            },
3146        );
3147    }
3148
3149    #[test]
3150    fn java_continue_lloc() {
3151        check_metrics::<JavaParser>(
3152            "int max = 10; // +1
3153
3154            for (int i = 0; i < max; i++) { // +1
3155                if(i % 2 == 0) { continue;} + 2
3156                System.out.println(i); // +1
3157             }",
3158            "foo.java",
3159            |metric| {
3160                // Spaces: 1
3161                insta::assert_json_snapshot!(
3162                    metric.loc,
3163                    @r###"
3164                    {
3165                      "sloc": 6.0,
3166                      "ploc": 5.0,
3167                      "lloc": 5.0,
3168                      "cloc": 3.0,
3169                      "blank": 1.0,
3170                      "sloc_average": 6.0,
3171                      "ploc_average": 5.0,
3172                      "lloc_average": 5.0,
3173                      "cloc_average": 3.0,
3174                      "blank_average": 1.0,
3175                      "sloc_min": 6.0,
3176                      "sloc_max": 6.0,
3177                      "cloc_min": 3.0,
3178                      "cloc_max": 3.0,
3179                      "ploc_min": 5.0,
3180                      "ploc_max": 5.0,
3181                      "lloc_min": 5.0,
3182                      "lloc_max": 5.0,
3183                      "blank_min": 1.0,
3184                      "blank_max": 1.0
3185                    }"###
3186                );
3187            },
3188        );
3189    }
3190
3191    #[test]
3192    fn java_try_lloc() {
3193        check_metrics::<JavaParser>(
3194            "try { // +1
3195                int[] myNumbers = {1, 2, 3}; // +1
3196                System.out.println(myNumbers[10]); // +1
3197              } catch (Exception e) {
3198                System.out.println(e.getMessage()); // +1
3199                throw e; // +1
3200              }",
3201            "foo.java",
3202            |metric| {
3203                // Spaces: 1
3204                insta::assert_json_snapshot!(
3205                    metric.loc,
3206                    @r###"
3207                    {
3208                      "sloc": 7.0,
3209                      "ploc": 7.0,
3210                      "lloc": 5.0,
3211                      "cloc": 5.0,
3212                      "blank": 0.0,
3213                      "sloc_average": 7.0,
3214                      "ploc_average": 7.0,
3215                      "lloc_average": 5.0,
3216                      "cloc_average": 5.0,
3217                      "blank_average": 0.0,
3218                      "sloc_min": 7.0,
3219                      "sloc_max": 7.0,
3220                      "cloc_min": 5.0,
3221                      "cloc_max": 5.0,
3222                      "ploc_min": 7.0,
3223                      "ploc_max": 7.0,
3224                      "lloc_min": 5.0,
3225                      "lloc_max": 5.0,
3226                      "blank_min": 0.0,
3227                      "blank_max": 0.0
3228                    }"###
3229                );
3230            },
3231        );
3232    }
3233
3234    #[test]
3235    fn java_class_loc() {
3236        check_metrics::<JavaParser>(
3237            "
3238            public class Person {
3239              private String name;
3240              public Person(String name){
3241                this.name = name; // +1
3242              }
3243              public String getName() {
3244                return name; // +1
3245              }
3246            }",
3247            "foo.java",
3248            |metric| {
3249                // Spaces: 4
3250                insta::assert_json_snapshot!(
3251                    metric.loc,
3252                    @r###"
3253                    {
3254                      "sloc": 9.0,
3255                      "ploc": 9.0,
3256                      "lloc": 2.0,
3257                      "cloc": 2.0,
3258                      "blank": 0.0,
3259                      "sloc_average": 2.25,
3260                      "ploc_average": 2.25,
3261                      "lloc_average": 0.5,
3262                      "cloc_average": 0.5,
3263                      "blank_average": 0.0,
3264                      "sloc_min": 9.0,
3265                      "sloc_max": 9.0,
3266                      "cloc_min": 2.0,
3267                      "cloc_max": 2.0,
3268                      "ploc_min": 9.0,
3269                      "ploc_max": 9.0,
3270                      "lloc_min": 2.0,
3271                      "lloc_max": 2.0,
3272                      "blank_min": 0.0,
3273                      "blank_max": 0.0
3274                    }"###
3275                );
3276            },
3277        );
3278    }
3279
3280    #[test]
3281    fn java_expressions_lloc() {
3282        check_metrics::<JavaParser>(
3283            "int x = 10;                                                            // +1 local var declaration
3284            x=+89;                                                                  // +1 expression statement
3285            int y = x * 2;                                                          // +1 local var declaration
3286            IntFunction double = (n) -> n*2;                                        // +1 local var declaration
3287            int y2 = double(x);                                                     // +1 local var declaration
3288            System.out.println(\"double \" + x + \" = \" + y2);                     // +1 expression statement
3289            String message = (x % 2) == 0 ? \"Evenly done.\" : \"Oddly done.\";     // +1 local var declaration
3290            Object done = (Runnable) () -> { System.out.println(\"Done!\"); };      // +2 local var declaration + expression statement
3291            String s = \"string\";                                                  // +1 local var declaration
3292            boolean isS = (s instanceof String);                                    // +1 local var declaration
3293            done.run();                                                             // +1 expression statement
3294            ",
3295            "foo.java",
3296            |metric| {
3297                // Spaces: 1
3298                insta::assert_json_snapshot!(
3299                    metric.loc,
3300                    @r###"
3301                    {
3302                      "sloc": 11.0,
3303                      "ploc": 11.0,
3304                      "lloc": 12.0,
3305                      "cloc": 11.0,
3306                      "blank": 0.0,
3307                      "sloc_average": 11.0,
3308                      "ploc_average": 11.0,
3309                      "lloc_average": 12.0,
3310                      "cloc_average": 11.0,
3311                      "blank_average": 0.0,
3312                      "sloc_min": 11.0,
3313                      "sloc_max": 11.0,
3314                      "cloc_min": 11.0,
3315                      "cloc_max": 11.0,
3316                      "ploc_min": 11.0,
3317                      "ploc_max": 11.0,
3318                      "lloc_min": 12.0,
3319                      "lloc_max": 12.0,
3320                      "blank_min": 0.0,
3321                      "blank_max": 0.0
3322                    }"###
3323                );
3324            },
3325        );
3326    }
3327
3328    #[test]
3329    fn java_statement_inline_loc() {
3330        check_metrics::<JavaParser>(
3331            "for (int i = 0; i < 100; i++) { System.out.println(\"hello\"); }",
3332            "foo.java",
3333            |metric| {
3334                // Spaces: 1
3335                insta::assert_json_snapshot!(
3336                    metric.loc,
3337                    @r###"
3338                    {
3339                      "sloc": 1.0,
3340                      "ploc": 1.0,
3341                      "lloc": 2.0,
3342                      "cloc": 0.0,
3343                      "blank": 0.0,
3344                      "sloc_average": 1.0,
3345                      "ploc_average": 1.0,
3346                      "lloc_average": 2.0,
3347                      "cloc_average": 0.0,
3348                      "blank_average": 0.0,
3349                      "sloc_min": 1.0,
3350                      "sloc_max": 1.0,
3351                      "cloc_min": 0.0,
3352                      "cloc_max": 0.0,
3353                      "ploc_min": 1.0,
3354                      "ploc_max": 1.0,
3355                      "lloc_min": 2.0,
3356                      "lloc_max": 2.0,
3357                      "blank_min": 0.0,
3358                      "blank_max": 0.0
3359                    }"###
3360                );
3361            },
3362        );
3363    }
3364
3365    #[test]
3366    fn java_general_loc() {
3367        check_metrics::<JavaParser>(
3368            "int max = 100;
3369
3370            /*
3371              Loop through and print
3372                from: 0
3373                to: max
3374            */
3375            for (int i = 0; i < max; i++) {
3376               // Print the value
3377               System.out.println(i);
3378             }",
3379            "foo.java",
3380            |metric| {
3381                // Spaces: 1
3382                insta::assert_json_snapshot!(
3383                    metric.loc,
3384                    @r###"
3385                    {
3386                      "sloc": 11.0,
3387                      "ploc": 4.0,
3388                      "lloc": 3.0,
3389                      "cloc": 6.0,
3390                      "blank": 1.0,
3391                      "sloc_average": 11.0,
3392                      "ploc_average": 4.0,
3393                      "lloc_average": 3.0,
3394                      "cloc_average": 6.0,
3395                      "blank_average": 1.0,
3396                      "sloc_min": 11.0,
3397                      "sloc_max": 11.0,
3398                      "cloc_min": 6.0,
3399                      "cloc_max": 6.0,
3400                      "ploc_min": 4.0,
3401                      "ploc_max": 4.0,
3402                      "lloc_min": 3.0,
3403                      "lloc_max": 3.0,
3404                      "blank_min": 1.0,
3405                      "blank_max": 1.0
3406                    }"###
3407                );
3408            },
3409        );
3410    }
3411
3412    #[test]
3413    fn java_main_class_loc() {
3414        check_metrics::<JavaParser>(
3415            "package com.company;
3416             /**
3417             * The HelloWorldApp class implements an application that
3418             * simply prints \"Hello World!\" to standard output.
3419             */
3420
3421            class HelloWorldApp {
3422              public void main(String[] args) {
3423                String message = args.length == 0 ? \"Hello empty world\" : \"Hello world\"; // +1 lloc : 1 var assignment
3424                System.out.println(message); // Display the string. +1 lloc
3425              }
3426            }",
3427            "foo.java",
3428            |metric| {
3429                // Spaces: 3
3430                insta::assert_json_snapshot!(
3431                    metric.loc,
3432                    @r###"
3433                    {
3434                      "sloc": 12.0,
3435                      "ploc": 7.0,
3436                      "lloc": 2.0,
3437                      "cloc": 6.0,
3438                      "blank": 1.0,
3439                      "sloc_average": 4.0,
3440                      "ploc_average": 2.3333333333333335,
3441                      "lloc_average": 0.6666666666666666,
3442                      "cloc_average": 2.0,
3443                      "blank_average": 0.3333333333333333,
3444                      "sloc_min": 6.0,
3445                      "sloc_max": 6.0,
3446                      "cloc_min": 2.0,
3447                      "cloc_max": 2.0,
3448                      "ploc_min": 6.0,
3449                      "ploc_max": 6.0,
3450                      "lloc_min": 2.0,
3451                      "lloc_max": 2.0,
3452                      "blank_min": 0.0,
3453                      "blank_max": 0.0
3454                    }"###
3455                );
3456            },
3457        );
3458    }
3459}