Skip to main content

rust_code_analysis_code_split/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 MozjsCode {
619    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
620        use Mozjs::*;
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 JavascriptCode {
645    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
646        use Javascript::*;
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 TypescriptCode {
671    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
672        use Typescript::*;
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 TsxCode {
697    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
698        use Tsx::*;
699
700        let (start, end) = init(node, stats, is_func_space, is_unit);
701
702        match node.kind_id().into() {
703            String | DQUOTE | Program => {}
704            Comment => {
705                add_cloc_lines(stats, start, end);
706            }
707            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
708            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
709            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
710            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
711            | StatementIdentifier => {
712                stats.lloc.logical_lines += 1;
713            }
714            _ => {
715                check_comment_ends_on_code_line(stats, start);
716                stats.ploc.lines.insert(start);
717            }
718        }
719    }
720}
721
722impl Loc for RustCode {
723    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
724        use Rust::*;
725
726        let (start, end) = init(node, stats, is_func_space, is_unit);
727
728        match node.kind_id().into() {
729            StringLiteral
730            | RawStringLiteral
731            | Block
732            | SourceFile
733            | SLASH
734            | SLASHSLASH
735            | SLASHSTAR
736            | STARSLASH
737            | OuterDocCommentMarker
738            | OuterDocCommentMarker2
739            | DocComment
740            | InnerDocCommentMarker
741            | BANG => {}
742            BlockComment => {
743                add_cloc_lines(stats, start, end);
744            }
745            LineComment => {
746                // Exclude the last line for `LineComment` containing a `DocComment`,
747                // since the `DocComment` includes the newline,
748                // as explained here: https://github.com/tree-sitter/tree-sitter-rust/blob/2eaf126458a4d6a69401089b6ba78c5e5d6c1ced/src/scanner.c#L194-L195
749                let end = if node.is_child(DocComment as u16) {
750                    end - 1
751                } else {
752                    end
753                };
754                add_cloc_lines(stats, start, end);
755            }
756            Statement
757            | EmptyStatement
758            | ExpressionStatement
759            | LetDeclaration
760            | AssignmentExpression
761            | CompoundAssignmentExpr => {
762                stats.lloc.logical_lines += 1;
763            }
764            _ => {
765                check_comment_ends_on_code_line(stats, start);
766                stats.ploc.lines.insert(start);
767            }
768        }
769    }
770}
771
772impl Loc for CppCode {
773    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
774        use Cpp::*;
775
776        let (start, end) = init(node, stats, is_func_space, is_unit);
777
778        match node.kind_id().into() {
779            RawStringLiteral | StringLiteral | DeclarationList | FieldDeclarationList
780            | TranslationUnit => {}
781            Comment => {
782                add_cloc_lines(stats, start, end);
783            }
784            WhileStatement | SwitchStatement | CaseStatement | IfStatement | ForStatement
785            | ReturnStatement | BreakStatement | ContinueStatement | GotoStatement
786            | ThrowStatement | TryStatement | TryStatement2 | ExpressionStatement
787            | ExpressionStatement2 | LabeledStatement | StatementIdentifier => {
788                stats.lloc.logical_lines += 1;
789            }
790            Declaration => {
791                if node.count_specific_ancestors::<CppParser>(
792                    |node| {
793                        matches!(
794                            node.kind_id().into(),
795                            WhileStatement | ForStatement | IfStatement
796                        )
797                    },
798                    |node| node.kind_id() == CompoundStatement,
799                ) == 0
800                {
801                    stats.lloc.logical_lines += 1;
802                }
803            }
804            _ => {
805                check_comment_ends_on_code_line(stats, start);
806                stats.ploc.lines.insert(start);
807
808                // As reported here: https://github.com/tree-sitter/tree-sitter-cpp/issues/276
809                // `tree-sitter-cpp` doesn't expand macros, providing a single `PreprocArg` node for the entire macro argument.
810                // Therefore, all lines from `start_row` to `end_row` must be added to PLOC to account for the unexpanded macro content
811                if let PreprocArg = node.kind_id().into() {
812                    (node.start_row() + 1..=node.end_row()).for_each(|line| {
813                        stats.ploc.lines.insert(line);
814                    });
815                }
816            }
817        }
818    }
819}
820
821impl Loc for JavaCode {
822    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
823        use Java::*;
824
825        let (start, end) = init(node, stats, is_func_space, is_unit);
826        let kind_id: Java = node.kind_id().into();
827        // LLOC in Java is counted for statements only
828        // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
829        match kind_id {
830            Program => {}
831            LineComment | BlockComment => {
832                add_cloc_lines(stats, start, end);
833            }
834            AssertStatement | BreakStatement | ContinueStatement | DoStatement
835            | EnhancedForStatement | ExpressionStatement | ForStatement | IfStatement
836            | ReturnStatement | SwitchExpression | ThrowStatement | TryStatement
837            | WhileStatement => {
838                stats.lloc.logical_lines += 1;
839            }
840            LocalVariableDeclaration => {
841                if node.count_specific_ancestors::<JavaParser>(
842                    |node| node.kind_id() == ForStatement,
843                    |node| node.kind_id() == Block,
844                ) == 0
845                {
846                    // The initializer, condition, and increment in a for loop are expressions.
847                    // Don't count the variable declaration if in a ForStatement.
848                    // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
849                    stats.lloc.logical_lines += 1;
850                }
851            }
852            _ => {
853                check_comment_ends_on_code_line(stats, start);
854                stats.ploc.lines.insert(start);
855            }
856        }
857    }
858}
859
860implement_metric_trait!(Loc, PreprocCode, CcommentCode, KotlinCode);
861
862#[cfg(test)]
863mod tests {
864    use crate::tools::check_metrics;
865
866    use super::*;
867
868    #[test]
869    fn python_sloc() {
870        check_metrics::<PythonParser>(
871            "
872
873            a = 42
874
875            ",
876            "foo.py",
877            |metric| {
878                // Spaces: 1
879                insta::assert_json_snapshot!(
880                    metric.loc,
881                    @r###"
882                    {
883                      "sloc": 1.0,
884                      "ploc": 1.0,
885                      "lloc": 1.0,
886                      "cloc": 0.0,
887                      "blank": 0.0,
888                      "sloc_average": 1.0,
889                      "ploc_average": 1.0,
890                      "lloc_average": 1.0,
891                      "cloc_average": 0.0,
892                      "blank_average": 0.0,
893                      "sloc_min": 1.0,
894                      "sloc_max": 1.0,
895                      "cloc_min": 0.0,
896                      "cloc_max": 0.0,
897                      "ploc_min": 1.0,
898                      "ploc_max": 1.0,
899                      "lloc_min": 1.0,
900                      "lloc_max": 1.0,
901                      "blank_min": 0.0,
902                      "blank_max": 0.0
903                    }"###
904                );
905            },
906        );
907    }
908
909    #[test]
910    fn python_blank() {
911        check_metrics::<PythonParser>(
912            "
913            a = 42
914
915            b = 43
916
917            ",
918            "foo.py",
919            |metric| {
920                // Spaces: 1
921                insta::assert_json_snapshot!(
922                    metric.loc,
923                    @r###"
924                    {
925                      "sloc": 3.0,
926                      "ploc": 2.0,
927                      "lloc": 2.0,
928                      "cloc": 0.0,
929                      "blank": 1.0,
930                      "sloc_average": 3.0,
931                      "ploc_average": 2.0,
932                      "lloc_average": 2.0,
933                      "cloc_average": 0.0,
934                      "blank_average": 1.0,
935                      "sloc_min": 3.0,
936                      "sloc_max": 3.0,
937                      "cloc_min": 0.0,
938                      "cloc_max": 0.0,
939                      "ploc_min": 2.0,
940                      "ploc_max": 2.0,
941                      "lloc_min": 2.0,
942                      "lloc_max": 2.0,
943                      "blank_min": 1.0,
944                      "blank_max": 1.0
945                    }"###
946                );
947            },
948        );
949    }
950
951    #[test]
952    fn rust_blank() {
953        check_metrics::<RustParser>(
954            "
955
956            let a = 42;
957
958            let b = 43;
959
960            ",
961            "foo.rs",
962            |metric| {
963                // Spaces: 1
964                insta::assert_json_snapshot!(
965                    metric.loc,
966                    @r###"
967                    {
968                      "sloc": 3.0,
969                      "ploc": 2.0,
970                      "lloc": 2.0,
971                      "cloc": 0.0,
972                      "blank": 1.0,
973                      "sloc_average": 3.0,
974                      "ploc_average": 2.0,
975                      "lloc_average": 2.0,
976                      "cloc_average": 0.0,
977                      "blank_average": 1.0,
978                      "sloc_min": 3.0,
979                      "sloc_max": 3.0,
980                      "cloc_min": 0.0,
981                      "cloc_max": 0.0,
982                      "ploc_min": 2.0,
983                      "ploc_max": 2.0,
984                      "lloc_min": 2.0,
985                      "lloc_max": 2.0,
986                      "blank_min": 1.0,
987                      "blank_max": 1.0
988                    }"###
989                );
990            },
991        );
992
993        check_metrics::<RustParser>("fn func() { /* comment */ }", "foo.rs", |metric| {
994            // Spaces: 2
995            insta::assert_json_snapshot!(
996                metric.loc,
997                @r###"
998                    {
999                      "sloc": 1.0,
1000                      "ploc": 1.0,
1001                      "lloc": 0.0,
1002                      "cloc": 1.0,
1003                      "blank": 0.0,
1004                      "sloc_average": 0.5,
1005                      "ploc_average": 0.5,
1006                      "lloc_average": 0.0,
1007                      "cloc_average": 0.5,
1008                      "blank_average": 0.0,
1009                      "sloc_min": 1.0,
1010                      "sloc_max": 1.0,
1011                      "cloc_min": 1.0,
1012                      "cloc_max": 1.0,
1013                      "ploc_min": 1.0,
1014                      "ploc_max": 1.0,
1015                      "lloc_min": 0.0,
1016                      "lloc_max": 0.0,
1017                      "blank_min": 0.0,
1018                      "blank_max": 0.0
1019                    }"###
1020            );
1021        });
1022    }
1023
1024    #[test]
1025    fn c_blank() {
1026        check_metrics::<CppParser>(
1027            "
1028
1029            int a = 42;
1030
1031            int b = 43;
1032
1033            ",
1034            "foo.c",
1035            |metric| {
1036                // Spaces: 1
1037                insta::assert_json_snapshot!(
1038                    metric.loc,
1039                    @r###"
1040                    {
1041                      "sloc": 3.0,
1042                      "ploc": 2.0,
1043                      "lloc": 2.0,
1044                      "cloc": 0.0,
1045                      "blank": 1.0,
1046                      "sloc_average": 3.0,
1047                      "ploc_average": 2.0,
1048                      "lloc_average": 2.0,
1049                      "cloc_average": 0.0,
1050                      "blank_average": 1.0,
1051                      "sloc_min": 3.0,
1052                      "sloc_max": 3.0,
1053                      "cloc_min": 0.0,
1054                      "cloc_max": 0.0,
1055                      "ploc_min": 2.0,
1056                      "ploc_max": 2.0,
1057                      "lloc_min": 2.0,
1058                      "lloc_max": 2.0,
1059                      "blank_min": 1.0,
1060                      "blank_max": 1.0
1061                    }"###
1062                );
1063            },
1064        );
1065    }
1066
1067    #[test]
1068    fn python_no_zero_blank() {
1069        // Checks that the blank metric is not equal to 0 when there are some
1070        // comments next to code lines.
1071        check_metrics::<PythonParser>(
1072            "def ConnectToUpdateServer():
1073                 pool = 4
1074
1075                 updateServer = -42
1076                 isConnected = False
1077                 currTry = 0
1078                 numRetries = 10 # Number of IPC connection retries before
1079                                 # giving up.
1080                 numTries = 20 # Number of IPC connection tries before
1081                               # giving up.",
1082            "foo.py",
1083            |metric| {
1084                // Spaces: 2
1085                insta::assert_json_snapshot!(
1086                    metric.loc,
1087                    @r#"
1088                {
1089                  "sloc": 10.0,
1090                  "ploc": 7.0,
1091                  "lloc": 6.0,
1092                  "cloc": 4.0,
1093                  "blank": 1.0,
1094                  "sloc_average": 5.0,
1095                  "ploc_average": 3.5,
1096                  "lloc_average": 3.0,
1097                  "cloc_average": 2.0,
1098                  "blank_average": 0.5,
1099                  "sloc_min": 1.0,
1100                  "sloc_max": 1.0,
1101                  "cloc_min": 0.0,
1102                  "cloc_max": 0.0,
1103                  "ploc_min": 1.0,
1104                  "ploc_max": 1.0,
1105                  "lloc_min": 0.0,
1106                  "lloc_max": 0.0,
1107                  "blank_min": 0.0,
1108                  "blank_max": 0.0
1109                }
1110                "#
1111                );
1112            },
1113        );
1114    }
1115
1116    #[test]
1117    fn python_no_blank() {
1118        // Checks that the blank metric is equal to 0 when there are no blank
1119        // lines and there are comments next to code lines.
1120        check_metrics::<PythonParser>(
1121            "def ConnectToUpdateServer():
1122                 pool = 4
1123                 updateServer = -42
1124                 isConnected = False
1125                 currTry = 0
1126                 numRetries = 10 # Number of IPC connection retries before
1127                                 # giving up.
1128                 numTries = 20 # Number of IPC connection tries before
1129                               # giving up.",
1130            "foo.py",
1131            |metric| {
1132                // Spaces: 2
1133                insta::assert_json_snapshot!(
1134                    metric.loc,
1135                    @r#"
1136                {
1137                  "sloc": 9.0,
1138                  "ploc": 7.0,
1139                  "lloc": 6.0,
1140                  "cloc": 4.0,
1141                  "blank": 0.0,
1142                  "sloc_average": 4.5,
1143                  "ploc_average": 3.5,
1144                  "lloc_average": 3.0,
1145                  "cloc_average": 2.0,
1146                  "blank_average": 0.0,
1147                  "sloc_min": 1.0,
1148                  "sloc_max": 1.0,
1149                  "cloc_min": 0.0,
1150                  "cloc_max": 0.0,
1151                  "ploc_min": 1.0,
1152                  "ploc_max": 1.0,
1153                  "lloc_min": 0.0,
1154                  "lloc_max": 0.0,
1155                  "blank_min": 0.0,
1156                  "blank_max": 0.0
1157                }
1158                "#
1159                );
1160            },
1161        );
1162    }
1163
1164    #[test]
1165    fn python_no_zero_blank_more_comments() {
1166        // Checks that the blank metric is not equal to 0 when there are more
1167        // comments next to code lines compared to the previous tests.
1168        check_metrics::<PythonParser>(
1169            "def ConnectToUpdateServer():
1170                 pool = 4
1171
1172                 updateServer = -42
1173                 isConnected = False
1174                 currTry = 0 # Set this variable to 0
1175                 numRetries = 10 # Number of IPC connection retries before
1176                                 # giving up.
1177                 numTries = 20 # Number of IPC connection tries before
1178                               # giving up.",
1179            "foo.py",
1180            |metric| {
1181                // Spaces: 2
1182                insta::assert_json_snapshot!(
1183                    metric.loc,
1184                    @r#"
1185                {
1186                  "sloc": 10.0,
1187                  "ploc": 7.0,
1188                  "lloc": 6.0,
1189                  "cloc": 5.0,
1190                  "blank": 1.0,
1191                  "sloc_average": 5.0,
1192                  "ploc_average": 3.5,
1193                  "lloc_average": 3.0,
1194                  "cloc_average": 2.5,
1195                  "blank_average": 0.5,
1196                  "sloc_min": 1.0,
1197                  "sloc_max": 1.0,
1198                  "cloc_min": 0.0,
1199                  "cloc_max": 0.0,
1200                  "ploc_min": 1.0,
1201                  "ploc_max": 1.0,
1202                  "lloc_min": 0.0,
1203                  "lloc_max": 0.0,
1204                  "blank_min": 0.0,
1205                  "blank_max": 0.0
1206                }
1207                "#
1208                );
1209            },
1210        );
1211    }
1212
1213    #[test]
1214    fn rust_no_zero_blank() {
1215        // Checks that the blank metric is not equal to 0 when there are some
1216        // comments next to code lines.
1217        check_metrics::<RustParser>(
1218            "fn ConnectToUpdateServer() {
1219              let pool = 0;
1220
1221              let updateServer = -42;
1222              let isConnected = false;
1223              let currTry = 0;
1224              let numRetries = 10;  // Number of IPC connection retries before
1225                                    // giving up.
1226              let numTries = 20;    // Number of IPC connection tries before
1227                                    // giving up.
1228            }",
1229            "foo.rs",
1230            |metric| {
1231                // Spaces: 2
1232                insta::assert_json_snapshot!(
1233                    metric.loc,
1234                    @r###"
1235                    {
1236                      "sloc": 11.0,
1237                      "ploc": 8.0,
1238                      "lloc": 6.0,
1239                      "cloc": 4.0,
1240                      "blank": 1.0,
1241                      "sloc_average": 5.5,
1242                      "ploc_average": 4.0,
1243                      "lloc_average": 3.0,
1244                      "cloc_average": 2.0,
1245                      "blank_average": 0.5,
1246                      "sloc_min": 11.0,
1247                      "sloc_max": 11.0,
1248                      "cloc_min": 4.0,
1249                      "cloc_max": 4.0,
1250                      "ploc_min": 8.0,
1251                      "ploc_max": 8.0,
1252                      "lloc_min": 6.0,
1253                      "lloc_max": 6.0,
1254                      "blank_min": 1.0,
1255                      "blank_max": 1.0
1256                    }"###
1257                );
1258            },
1259        );
1260    }
1261
1262    #[test]
1263    fn javascript_no_zero_blank() {
1264        // Checks that the blank metric is not equal to 0 when there are some
1265        // comments next to code lines.
1266        check_metrics::<JavascriptParser>(
1267            "function ConnectToUpdateServer() {
1268              var pool = 0;
1269
1270              var updateServer = -42;
1271              var isConnected = false;
1272              var currTry = 0;
1273              var numRetries = 10;  // Number of IPC connection retries before
1274                                    // giving up.
1275              var numTries = 20;    // Number of IPC connection tries before
1276                                    // giving up.
1277            }",
1278            "foo.js",
1279            |metric| {
1280                // Spaces: 2
1281                insta::assert_json_snapshot!(
1282                    metric.loc,
1283                    @r#"
1284                {
1285                  "sloc": 11.0,
1286                  "ploc": 10.0,
1287                  "lloc": 1.0,
1288                  "cloc": 0.0,
1289                  "blank": 1.0,
1290                  "sloc_average": 11.0,
1291                  "ploc_average": 10.0,
1292                  "lloc_average": 1.0,
1293                  "cloc_average": 0.0,
1294                  "blank_average": 1.0,
1295                  "sloc_min": 11.0,
1296                  "sloc_max": 11.0,
1297                  "cloc_min": 0.0,
1298                  "cloc_max": 0.0,
1299                  "ploc_min": 10.0,
1300                  "ploc_max": 10.0,
1301                  "lloc_min": 1.0,
1302                  "lloc_max": 1.0,
1303                  "blank_min": 1.0,
1304                  "blank_max": 1.0
1305                }
1306                "#
1307                );
1308            },
1309        );
1310    }
1311
1312    #[test]
1313    fn cpp_no_zero_blank() {
1314        // Checks that the blank metric is not equal to 0 when there are some
1315        // comments next to code lines.
1316        check_metrics::<CppParser>(
1317            "void ConnectToUpdateServer() {
1318              int pool;
1319
1320              int updateServer = -42;
1321              bool isConnected = false;
1322              int currTry = 0;
1323              const int numRetries = 10; // Number of IPC connection retries before
1324                                         // giving up.
1325              const int numTries = 20; // Number of IPC connection tries before
1326                                       // giving up.
1327            }",
1328            "foo.cpp",
1329            |metric| {
1330                // Spaces: 2
1331                insta::assert_json_snapshot!(
1332                    metric.loc,
1333                    @r###"
1334                    {
1335                      "sloc": 11.0,
1336                      "ploc": 8.0,
1337                      "lloc": 6.0,
1338                      "cloc": 4.0,
1339                      "blank": 1.0,
1340                      "sloc_average": 5.5,
1341                      "ploc_average": 4.0,
1342                      "lloc_average": 3.0,
1343                      "cloc_average": 2.0,
1344                      "blank_average": 0.5,
1345                      "sloc_min": 11.0,
1346                      "sloc_max": 11.0,
1347                      "cloc_min": 4.0,
1348                      "cloc_max": 4.0,
1349                      "ploc_min": 8.0,
1350                      "ploc_max": 8.0,
1351                      "lloc_min": 6.0,
1352                      "lloc_max": 6.0,
1353                      "blank_min": 1.0,
1354                      "blank_max": 1.0
1355                    }"###
1356                );
1357            },
1358        );
1359    }
1360
1361    #[test]
1362    fn cpp_code_line_start_block_blank() {
1363        // Checks that the blank metric is equal to 1 when there are
1364        // block comments starting next to code lines.
1365        check_metrics::<CppParser>(
1366            "void ConnectToUpdateServer() {
1367              int pool;
1368
1369              int updateServer = -42;
1370              bool isConnected = false;
1371              int currTry = 0;
1372              const int numRetries = 10; /* Number of IPC connection retries
1373              before
1374              giving up. */
1375              const int numTries = 20; // Number of IPC connection tries before
1376                                       // giving up.
1377            }",
1378            "foo.cpp",
1379            |metric| {
1380                // Spaces: 2
1381                insta::assert_json_snapshot!(
1382                    metric.loc,
1383                    @r###"
1384                    {
1385                      "sloc": 12.0,
1386                      "ploc": 8.0,
1387                      "lloc": 6.0,
1388                      "cloc": 5.0,
1389                      "blank": 1.0,
1390                      "sloc_average": 6.0,
1391                      "ploc_average": 4.0,
1392                      "lloc_average": 3.0,
1393                      "cloc_average": 2.5,
1394                      "blank_average": 0.5,
1395                      "sloc_min": 12.0,
1396                      "sloc_max": 12.0,
1397                      "cloc_min": 5.0,
1398                      "cloc_max": 5.0,
1399                      "ploc_min": 8.0,
1400                      "ploc_max": 8.0,
1401                      "lloc_min": 6.0,
1402                      "lloc_max": 6.0,
1403                      "blank_min": 1.0,
1404                      "blank_max": 1.0
1405                    }"###
1406                );
1407            },
1408        );
1409    }
1410
1411    #[test]
1412    fn cpp_block_comment_blank() {
1413        // Checks that the blank metric is equal to 1 when there are
1414        // block comments on independent lines.
1415        check_metrics::<CppParser>(
1416            "void ConnectToUpdateServer() {
1417              int pool;
1418
1419              int updateServer = -42;
1420              bool isConnected = false;
1421              int currTry = 0;
1422              /* Number of IPC connection retries
1423              before
1424              giving up. */
1425              const int numRetries = 10;
1426              const int numTries = 20; // Number of IPC connection tries before
1427                                       // giving up.
1428            }",
1429            "foo.cpp",
1430            |metric| {
1431                // Spaces: 2
1432                insta::assert_json_snapshot!(
1433                    metric.loc,
1434                    @r###"
1435                    {
1436                      "sloc": 13.0,
1437                      "ploc": 8.0,
1438                      "lloc": 6.0,
1439                      "cloc": 5.0,
1440                      "blank": 1.0,
1441                      "sloc_average": 6.5,
1442                      "ploc_average": 4.0,
1443                      "lloc_average": 3.0,
1444                      "cloc_average": 2.5,
1445                      "blank_average": 0.5,
1446                      "sloc_min": 13.0,
1447                      "sloc_max": 13.0,
1448                      "cloc_min": 5.0,
1449                      "cloc_max": 5.0,
1450                      "ploc_min": 8.0,
1451                      "ploc_max": 8.0,
1452                      "lloc_min": 6.0,
1453                      "lloc_max": 6.0,
1454                      "blank_min": 1.0,
1455                      "blank_max": 1.0
1456                    }"###
1457                );
1458            },
1459        );
1460    }
1461
1462    #[test]
1463    fn cpp_code_line_block_one_line_blank() {
1464        // Checks that the blank metric is equal to 1 when there are
1465        // block comments before the same code line.
1466        check_metrics::<CppParser>(
1467            "void ConnectToUpdateServer() {
1468              int pool;
1469
1470              int updateServer = -42;
1471              bool isConnected = false;
1472              int currTry = 0;
1473              /* Number of IPC connection retries before giving up. */ const int numRetries = 10;
1474              const int numTries = 20; // Number of IPC connection tries before
1475                                       // giving up.
1476            }",
1477            "foo.cpp",
1478            |metric| {
1479                // Spaces: 2
1480                insta::assert_json_snapshot!(
1481                    metric.loc,
1482                    @r###"
1483                    {
1484                      "sloc": 10.0,
1485                      "ploc": 8.0,
1486                      "lloc": 6.0,
1487                      "cloc": 3.0,
1488                      "blank": 1.0,
1489                      "sloc_average": 5.0,
1490                      "ploc_average": 4.0,
1491                      "lloc_average": 3.0,
1492                      "cloc_average": 1.5,
1493                      "blank_average": 0.5,
1494                      "sloc_min": 10.0,
1495                      "sloc_max": 10.0,
1496                      "cloc_min": 3.0,
1497                      "cloc_max": 3.0,
1498                      "ploc_min": 8.0,
1499                      "ploc_max": 8.0,
1500                      "lloc_min": 6.0,
1501                      "lloc_max": 6.0,
1502                      "blank_min": 1.0,
1503                      "blank_max": 1.0
1504                    }"###
1505                );
1506            },
1507        );
1508    }
1509
1510    #[test]
1511    fn cpp_code_line_end_block_blank() {
1512        // Checks that the blank metric is equal to 1 when there are
1513        // block comments ending next to code lines.
1514        check_metrics::<CppParser>(
1515            "void ConnectToUpdateServer() {
1516              int pool;
1517
1518              int updateServer = -42;
1519              bool isConnected = false;
1520              int currTry = 0;
1521              /* Number of IPC connection retries
1522              before
1523              giving up. */ const int numRetries = 10;
1524              const int numTries = 20; // Number of IPC connection tries before
1525                                       // giving up.
1526            }",
1527            "foo.cpp",
1528            |metric| {
1529                // Spaces: 2
1530                insta::assert_json_snapshot!(
1531                    metric.loc,
1532                    @r###"
1533                    {
1534                      "sloc": 12.0,
1535                      "ploc": 8.0,
1536                      "lloc": 6.0,
1537                      "cloc": 5.0,
1538                      "blank": 1.0,
1539                      "sloc_average": 6.0,
1540                      "ploc_average": 4.0,
1541                      "lloc_average": 3.0,
1542                      "cloc_average": 2.5,
1543                      "blank_average": 0.5,
1544                      "sloc_min": 12.0,
1545                      "sloc_max": 12.0,
1546                      "cloc_min": 5.0,
1547                      "cloc_max": 5.0,
1548                      "ploc_min": 8.0,
1549                      "ploc_max": 8.0,
1550                      "lloc_min": 6.0,
1551                      "lloc_max": 6.0,
1552                      "blank_min": 1.0,
1553                      "blank_max": 1.0
1554                    }"###
1555                );
1556            },
1557        );
1558    }
1559
1560    #[test]
1561    fn python_cloc() {
1562        check_metrics::<PythonParser>(
1563            "\"\"\"Block comment
1564            Block comment
1565            \"\"\"
1566            # Line Comment
1567            a = 42 # Line Comment",
1568            "foo.py",
1569            |metric| {
1570                // Spaces: 1
1571                insta::assert_json_snapshot!(
1572                    metric.loc,
1573                    @r#"
1574                {
1575                  "sloc": 5.0,
1576                  "ploc": 2.0,
1577                  "lloc": 2.0,
1578                  "cloc": 2.0,
1579                  "blank": 2.0,
1580                  "sloc_average": 5.0,
1581                  "ploc_average": 2.0,
1582                  "lloc_average": 2.0,
1583                  "cloc_average": 2.0,
1584                  "blank_average": 2.0,
1585                  "sloc_min": 5.0,
1586                  "sloc_max": 5.0,
1587                  "cloc_min": 2.0,
1588                  "cloc_max": 2.0,
1589                  "ploc_min": 2.0,
1590                  "ploc_max": 2.0,
1591                  "lloc_min": 2.0,
1592                  "lloc_max": 2.0,
1593                  "blank_min": 2.0,
1594                  "blank_max": 2.0
1595                }
1596                "#
1597                );
1598            },
1599        );
1600    }
1601
1602    #[test]
1603    fn rust_cloc() {
1604        check_metrics::<RustParser>(
1605            "/*Block comment
1606            Block Comment*/
1607            //Line Comment
1608            /*Block Comment*/ let a = 42; // Line Comment",
1609            "foo.rs",
1610            |metric| {
1611                // Spaces: 1
1612                insta::assert_json_snapshot!(
1613                    metric.loc,
1614                    @r###"
1615                    {
1616                      "sloc": 4.0,
1617                      "ploc": 1.0,
1618                      "lloc": 1.0,
1619                      "cloc": 5.0,
1620                      "blank": 0.0,
1621                      "sloc_average": 4.0,
1622                      "ploc_average": 1.0,
1623                      "lloc_average": 1.0,
1624                      "cloc_average": 5.0,
1625                      "blank_average": 0.0,
1626                      "sloc_min": 4.0,
1627                      "sloc_max": 4.0,
1628                      "cloc_min": 5.0,
1629                      "cloc_max": 5.0,
1630                      "ploc_min": 1.0,
1631                      "ploc_max": 1.0,
1632                      "lloc_min": 1.0,
1633                      "lloc_max": 1.0,
1634                      "blank_min": 0.0,
1635                      "blank_max": 0.0
1636                    }"###
1637                );
1638            },
1639        );
1640    }
1641
1642    #[test]
1643    fn c_cloc() {
1644        check_metrics::<CppParser>(
1645            "/*Block comment
1646            Block Comment*/
1647            //Line Comment
1648            /*Block Comment*/ int a = 42; // Line Comment",
1649            "foo.c",
1650            |metric| {
1651                // Spaces: 1
1652                insta::assert_json_snapshot!(
1653                    metric.loc,
1654                    @r###"
1655                    {
1656                      "sloc": 4.0,
1657                      "ploc": 1.0,
1658                      "lloc": 1.0,
1659                      "cloc": 5.0,
1660                      "blank": 0.0,
1661                      "sloc_average": 4.0,
1662                      "ploc_average": 1.0,
1663                      "lloc_average": 1.0,
1664                      "cloc_average": 5.0,
1665                      "blank_average": 0.0,
1666                      "sloc_min": 4.0,
1667                      "sloc_max": 4.0,
1668                      "cloc_min": 5.0,
1669                      "cloc_max": 5.0,
1670                      "ploc_min": 1.0,
1671                      "ploc_max": 1.0,
1672                      "lloc_min": 1.0,
1673                      "lloc_max": 1.0,
1674                      "blank_min": 0.0,
1675                      "blank_max": 0.0
1676                    }"###
1677                );
1678            },
1679        );
1680    }
1681
1682    #[test]
1683    fn python_lloc() {
1684        check_metrics::<PythonParser>(
1685            "for x in range(0,42):
1686                if x % 2 == 0:
1687                    print(x)",
1688            "foo.py",
1689            |metric| {
1690                // Spaces: 1
1691                insta::assert_json_snapshot!(
1692                    metric.loc,
1693                    @r###"
1694                    {
1695                      "sloc": 3.0,
1696                      "ploc": 3.0,
1697                      "lloc": 3.0,
1698                      "cloc": 0.0,
1699                      "blank": 0.0,
1700                      "sloc_average": 3.0,
1701                      "ploc_average": 3.0,
1702                      "lloc_average": 3.0,
1703                      "cloc_average": 0.0,
1704                      "blank_average": 0.0,
1705                      "sloc_min": 3.0,
1706                      "sloc_max": 3.0,
1707                      "cloc_min": 0.0,
1708                      "cloc_max": 0.0,
1709                      "ploc_min": 3.0,
1710                      "ploc_max": 3.0,
1711                      "lloc_min": 3.0,
1712                      "lloc_max": 3.0,
1713                      "blank_min": 0.0,
1714                      "blank_max": 0.0
1715                    }"###
1716                );
1717            },
1718        );
1719    }
1720
1721    #[test]
1722    fn rust_lloc() {
1723        check_metrics::<RustParser>(
1724            "for x in 0..42 {
1725                if x % 2 == 0 {
1726                    println!(\"{}\", x);
1727                }
1728             }",
1729            "foo.rs",
1730            |metric| {
1731                // Spaces: 1
1732                insta::assert_json_snapshot!(
1733                    metric.loc,
1734                    @r###"
1735                    {
1736                      "sloc": 5.0,
1737                      "ploc": 5.0,
1738                      "lloc": 3.0,
1739                      "cloc": 0.0,
1740                      "blank": 0.0,
1741                      "sloc_average": 5.0,
1742                      "ploc_average": 5.0,
1743                      "lloc_average": 3.0,
1744                      "cloc_average": 0.0,
1745                      "blank_average": 0.0,
1746                      "sloc_min": 5.0,
1747                      "sloc_max": 5.0,
1748                      "cloc_min": 0.0,
1749                      "cloc_max": 0.0,
1750                      "ploc_min": 5.0,
1751                      "ploc_max": 5.0,
1752                      "lloc_min": 3.0,
1753                      "lloc_max": 3.0,
1754                      "blank_min": 0.0,
1755                      "blank_max": 0.0
1756                    }"###
1757                );
1758            },
1759        );
1760
1761        // LLOC returns three because there is an empty Rust statement
1762        check_metrics::<RustParser>(
1763            "let a = 42;
1764             if true {
1765                42
1766             } else {
1767                43
1768             };",
1769            "foo.rs",
1770            |metric| {
1771                // Spaces: 1
1772                insta::assert_json_snapshot!(
1773                    metric.loc,
1774                    @r###"
1775                    {
1776                      "sloc": 6.0,
1777                      "ploc": 6.0,
1778                      "lloc": 3.0,
1779                      "cloc": 0.0,
1780                      "blank": 0.0,
1781                      "sloc_average": 6.0,
1782                      "ploc_average": 6.0,
1783                      "lloc_average": 3.0,
1784                      "cloc_average": 0.0,
1785                      "blank_average": 0.0,
1786                      "sloc_min": 6.0,
1787                      "sloc_max": 6.0,
1788                      "cloc_min": 0.0,
1789                      "cloc_max": 0.0,
1790                      "ploc_min": 6.0,
1791                      "ploc_max": 6.0,
1792                      "lloc_min": 3.0,
1793                      "lloc_max": 3.0,
1794                      "blank_min": 0.0,
1795                      "blank_max": 0.0
1796                    }"###
1797                );
1798            },
1799        );
1800    }
1801
1802    #[test]
1803    fn c_lloc() {
1804        check_metrics::<CppParser>(
1805            "for (;;)
1806                break;",
1807            "foo.c",
1808            |metric| {
1809                // Spaces: 1
1810                insta::assert_json_snapshot!(
1811                    metric.loc,
1812                    @r###"
1813                    {
1814                      "sloc": 2.0,
1815                      "ploc": 2.0,
1816                      "lloc": 2.0,
1817                      "cloc": 0.0,
1818                      "blank": 0.0,
1819                      "sloc_average": 2.0,
1820                      "ploc_average": 2.0,
1821                      "lloc_average": 2.0,
1822                      "cloc_average": 0.0,
1823                      "blank_average": 0.0,
1824                      "sloc_min": 2.0,
1825                      "sloc_max": 2.0,
1826                      "cloc_min": 0.0,
1827                      "cloc_max": 0.0,
1828                      "ploc_min": 2.0,
1829                      "ploc_max": 2.0,
1830                      "lloc_min": 2.0,
1831                      "lloc_max": 2.0,
1832                      "blank_min": 0.0,
1833                      "blank_max": 0.0
1834                    }"###
1835                );
1836            },
1837        );
1838    }
1839
1840    #[test]
1841    fn cpp_lloc() {
1842        check_metrics::<CppParser>(
1843            "nsTArray<xpcGCCallback> callbacks(extraGCCallbacks.Clone());
1844             for (uint32_t i = 0; i < callbacks.Length(); ++i) {
1845                 callbacks[i](status);
1846             }",
1847            "foo.cpp",
1848            |metric| {
1849                // Spaces: 1
1850                // lloc: nsTArray, for, callbacks
1851                insta::assert_json_snapshot!(
1852                    metric.loc,
1853                    @r###"
1854                    {
1855                      "sloc": 4.0,
1856                      "ploc": 4.0,
1857                      "lloc": 3.0,
1858                      "cloc": 0.0,
1859                      "blank": 0.0,
1860                      "sloc_average": 4.0,
1861                      "ploc_average": 4.0,
1862                      "lloc_average": 3.0,
1863                      "cloc_average": 0.0,
1864                      "blank_average": 0.0,
1865                      "sloc_min": 4.0,
1866                      "sloc_max": 4.0,
1867                      "cloc_min": 0.0,
1868                      "cloc_max": 0.0,
1869                      "ploc_min": 4.0,
1870                      "ploc_max": 4.0,
1871                      "lloc_min": 3.0,
1872                      "lloc_max": 3.0,
1873                      "blank_min": 0.0,
1874                      "blank_max": 0.0
1875                    }"###
1876                );
1877            },
1878        );
1879    }
1880
1881    #[test]
1882    fn cpp_return_lloc() {
1883        check_metrics::<CppParser>(
1884            "uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
1885             return RgbaColor(pixel_data) == blank_pixel_;",
1886            "foo.cpp",
1887            |metric| {
1888                // Spaces: 1
1889                // lloc: pixel_data, return
1890                insta::assert_json_snapshot!(
1891                    metric.loc,
1892                    @r###"
1893                    {
1894                      "sloc": 2.0,
1895                      "ploc": 2.0,
1896                      "lloc": 2.0,
1897                      "cloc": 0.0,
1898                      "blank": 0.0,
1899                      "sloc_average": 2.0,
1900                      "ploc_average": 2.0,
1901                      "lloc_average": 2.0,
1902                      "cloc_average": 0.0,
1903                      "blank_average": 0.0,
1904                      "sloc_min": 2.0,
1905                      "sloc_max": 2.0,
1906                      "cloc_min": 0.0,
1907                      "cloc_max": 0.0,
1908                      "ploc_min": 2.0,
1909                      "ploc_max": 2.0,
1910                      "lloc_min": 2.0,
1911                      "lloc_max": 2.0,
1912                      "blank_min": 0.0,
1913                      "blank_max": 0.0
1914                    }"###
1915                );
1916            },
1917        );
1918    }
1919
1920    #[test]
1921    fn cpp_for_lloc() {
1922        check_metrics::<CppParser>(
1923            "for (; start != end; ++start) {
1924                 const unsigned char idx = *start;
1925                 if (idx > 127 || !kValidTokenMap[idx]) return false;
1926             }",
1927            "foo.cpp",
1928            |metric| {
1929                // Spaces: 1
1930                // lloc: for, idx, if, return
1931                insta::assert_json_snapshot!(
1932                    metric.loc,
1933                    @r###"
1934                    {
1935                      "sloc": 4.0,
1936                      "ploc": 4.0,
1937                      "lloc": 4.0,
1938                      "cloc": 0.0,
1939                      "blank": 0.0,
1940                      "sloc_average": 4.0,
1941                      "ploc_average": 4.0,
1942                      "lloc_average": 4.0,
1943                      "cloc_average": 0.0,
1944                      "blank_average": 0.0,
1945                      "sloc_min": 4.0,
1946                      "sloc_max": 4.0,
1947                      "cloc_min": 0.0,
1948                      "cloc_max": 0.0,
1949                      "ploc_min": 4.0,
1950                      "ploc_max": 4.0,
1951                      "lloc_min": 4.0,
1952                      "lloc_max": 4.0,
1953                      "blank_min": 0.0,
1954                      "blank_max": 0.0
1955                    }"###
1956                );
1957            },
1958        );
1959    }
1960
1961    #[test]
1962    fn cpp_while_lloc() {
1963        check_metrics::<CppParser>(
1964            "while (sHeapAtoms) {
1965                 HttpHeapAtom* next = sHeapAtoms->next;
1966                 free(sHeapAtoms);
1967            }",
1968            "foo.cpp",
1969            |metric| {
1970                // Spaces: 1
1971                // lloc: while, next, free
1972                insta::assert_json_snapshot!(
1973                    metric.loc,
1974                    @r###"
1975                    {
1976                      "sloc": 4.0,
1977                      "ploc": 4.0,
1978                      "lloc": 3.0,
1979                      "cloc": 0.0,
1980                      "blank": 0.0,
1981                      "sloc_average": 4.0,
1982                      "ploc_average": 4.0,
1983                      "lloc_average": 3.0,
1984                      "cloc_average": 0.0,
1985                      "blank_average": 0.0,
1986                      "sloc_min": 4.0,
1987                      "sloc_max": 4.0,
1988                      "cloc_min": 0.0,
1989                      "cloc_max": 0.0,
1990                      "ploc_min": 4.0,
1991                      "ploc_max": 4.0,
1992                      "lloc_min": 3.0,
1993                      "lloc_max": 3.0,
1994                      "blank_min": 0.0,
1995                      "blank_max": 0.0
1996                    }"###
1997                );
1998            },
1999        );
2000    }
2001
2002    #[test]
2003    fn python_string_on_new_line() {
2004        // More lines of the same instruction were counted as blank lines
2005        check_metrics::<PythonParser>(
2006            "capabilities[\"goog:chromeOptions\"][\"androidPackage\"] = \\
2007                \"org.chromium.weblayer.shell\"",
2008            "foo.py",
2009            |metric| {
2010                // Spaces: 1
2011                insta::assert_json_snapshot!(
2012                    metric.loc,
2013                    @r###"
2014                    {
2015                      "sloc": 2.0,
2016                      "ploc": 2.0,
2017                      "lloc": 1.0,
2018                      "cloc": 0.0,
2019                      "blank": 0.0,
2020                      "sloc_average": 2.0,
2021                      "ploc_average": 2.0,
2022                      "lloc_average": 1.0,
2023                      "cloc_average": 0.0,
2024                      "blank_average": 0.0,
2025                      "sloc_min": 2.0,
2026                      "sloc_max": 2.0,
2027                      "cloc_min": 0.0,
2028                      "cloc_max": 0.0,
2029                      "ploc_min": 2.0,
2030                      "ploc_max": 2.0,
2031                      "lloc_min": 1.0,
2032                      "lloc_max": 1.0,
2033                      "blank_min": 0.0,
2034                      "blank_max": 0.0
2035                    }"###
2036                );
2037            },
2038        );
2039    }
2040
2041    #[test]
2042    fn rust_no_field_expression_lloc() {
2043        check_metrics::<RustParser>(
2044            "struct Foo {
2045                field: usize,
2046             }
2047             let foo = Foo { 42 };
2048             foo.field;",
2049            "foo.rs",
2050            |metric| {
2051                // Spaces: 1
2052                insta::assert_json_snapshot!(
2053                    metric.loc,
2054                    @r###"
2055                    {
2056                      "sloc": 5.0,
2057                      "ploc": 5.0,
2058                      "lloc": 2.0,
2059                      "cloc": 0.0,
2060                      "blank": 0.0,
2061                      "sloc_average": 5.0,
2062                      "ploc_average": 5.0,
2063                      "lloc_average": 2.0,
2064                      "cloc_average": 0.0,
2065                      "blank_average": 0.0,
2066                      "sloc_min": 5.0,
2067                      "sloc_max": 5.0,
2068                      "cloc_min": 0.0,
2069                      "cloc_max": 0.0,
2070                      "ploc_min": 5.0,
2071                      "ploc_max": 5.0,
2072                      "lloc_min": 2.0,
2073                      "lloc_max": 2.0,
2074                      "blank_min": 0.0,
2075                      "blank_max": 0.0
2076                    }"###
2077                );
2078            },
2079        );
2080    }
2081
2082    #[test]
2083    fn rust_no_parenthesized_expression_lloc() {
2084        check_metrics::<RustParser>("let a = (42 + 0);", "foo.rs", |metric| {
2085            // Spaces: 1
2086            insta::assert_json_snapshot!(
2087                metric.loc,
2088                @r###"
2089                    {
2090                      "sloc": 1.0,
2091                      "ploc": 1.0,
2092                      "lloc": 1.0,
2093                      "cloc": 0.0,
2094                      "blank": 0.0,
2095                      "sloc_average": 1.0,
2096                      "ploc_average": 1.0,
2097                      "lloc_average": 1.0,
2098                      "cloc_average": 0.0,
2099                      "blank_average": 0.0,
2100                      "sloc_min": 1.0,
2101                      "sloc_max": 1.0,
2102                      "cloc_min": 0.0,
2103                      "cloc_max": 0.0,
2104                      "ploc_min": 1.0,
2105                      "ploc_max": 1.0,
2106                      "lloc_min": 1.0,
2107                      "lloc_max": 1.0,
2108                      "blank_min": 0.0,
2109                      "blank_max": 0.0
2110                    }"###
2111            );
2112        });
2113    }
2114
2115    #[test]
2116    fn rust_no_array_expression_lloc() {
2117        check_metrics::<RustParser>("let a = [0; 42];", "foo.rs", |metric| {
2118            // Spaces: 1
2119            insta::assert_json_snapshot!(
2120                metric.loc,
2121                @r###"
2122                    {
2123                      "sloc": 1.0,
2124                      "ploc": 1.0,
2125                      "lloc": 1.0,
2126                      "cloc": 0.0,
2127                      "blank": 0.0,
2128                      "sloc_average": 1.0,
2129                      "ploc_average": 1.0,
2130                      "lloc_average": 1.0,
2131                      "cloc_average": 0.0,
2132                      "blank_average": 0.0,
2133                      "sloc_min": 1.0,
2134                      "sloc_max": 1.0,
2135                      "cloc_min": 0.0,
2136                      "cloc_max": 0.0,
2137                      "ploc_min": 1.0,
2138                      "ploc_max": 1.0,
2139                      "lloc_min": 1.0,
2140                      "lloc_max": 1.0,
2141                      "blank_min": 0.0,
2142                      "blank_max": 0.0
2143                    }"###
2144            );
2145        });
2146    }
2147
2148    #[test]
2149    fn rust_no_tuple_expression_lloc() {
2150        check_metrics::<RustParser>("let a = (0, 42);", "foo.rs", |metric| {
2151            // Spaces: 1
2152            insta::assert_json_snapshot!(
2153                metric.loc,
2154                @r###"
2155                    {
2156                      "sloc": 1.0,
2157                      "ploc": 1.0,
2158                      "lloc": 1.0,
2159                      "cloc": 0.0,
2160                      "blank": 0.0,
2161                      "sloc_average": 1.0,
2162                      "ploc_average": 1.0,
2163                      "lloc_average": 1.0,
2164                      "cloc_average": 0.0,
2165                      "blank_average": 0.0,
2166                      "sloc_min": 1.0,
2167                      "sloc_max": 1.0,
2168                      "cloc_min": 0.0,
2169                      "cloc_max": 0.0,
2170                      "ploc_min": 1.0,
2171                      "ploc_max": 1.0,
2172                      "lloc_min": 1.0,
2173                      "lloc_max": 1.0,
2174                      "blank_min": 0.0,
2175                      "blank_max": 0.0
2176                    }"###
2177            );
2178        });
2179    }
2180
2181    #[test]
2182    fn rust_no_unit_expression_lloc() {
2183        check_metrics::<RustParser>("let a = ();", "foo.rs", |metric| {
2184            // Spaces: 1
2185            insta::assert_json_snapshot!(
2186                metric.loc,
2187                @r###"
2188                    {
2189                      "sloc": 1.0,
2190                      "ploc": 1.0,
2191                      "lloc": 1.0,
2192                      "cloc": 0.0,
2193                      "blank": 0.0,
2194                      "sloc_average": 1.0,
2195                      "ploc_average": 1.0,
2196                      "lloc_average": 1.0,
2197                      "cloc_average": 0.0,
2198                      "blank_average": 0.0,
2199                      "sloc_min": 1.0,
2200                      "sloc_max": 1.0,
2201                      "cloc_min": 0.0,
2202                      "cloc_max": 0.0,
2203                      "ploc_min": 1.0,
2204                      "ploc_max": 1.0,
2205                      "lloc_min": 1.0,
2206                      "lloc_max": 1.0,
2207                      "blank_min": 0.0,
2208                      "blank_max": 0.0
2209                    }"###
2210            );
2211        });
2212    }
2213
2214    #[test]
2215    fn rust_call_function_lloc() {
2216        check_metrics::<RustParser>(
2217            "let a = foo(); // +1
2218             foo(); // +1
2219             k!(foo()); // +1",
2220            "foo.rs",
2221            |metric| {
2222                // Spaces: 1
2223                insta::assert_json_snapshot!(
2224                    metric.loc,
2225                    @r###"
2226                    {
2227                      "sloc": 3.0,
2228                      "ploc": 3.0,
2229                      "lloc": 3.0,
2230                      "cloc": 3.0,
2231                      "blank": 0.0,
2232                      "sloc_average": 3.0,
2233                      "ploc_average": 3.0,
2234                      "lloc_average": 3.0,
2235                      "cloc_average": 3.0,
2236                      "blank_average": 0.0,
2237                      "sloc_min": 3.0,
2238                      "sloc_max": 3.0,
2239                      "cloc_min": 3.0,
2240                      "cloc_max": 3.0,
2241                      "ploc_min": 3.0,
2242                      "ploc_max": 3.0,
2243                      "lloc_min": 3.0,
2244                      "lloc_max": 3.0,
2245                      "blank_min": 0.0,
2246                      "blank_max": 0.0
2247                    }"###
2248                );
2249            },
2250        );
2251    }
2252
2253    #[test]
2254    fn rust_macro_invocation_lloc() {
2255        check_metrics::<RustParser>(
2256            "let a = foo!(); // +1
2257             foo!(); // +1
2258             k(foo!()); // +1",
2259            "foo.rs",
2260            |metric| {
2261                // Spaces: 1
2262                insta::assert_json_snapshot!(
2263                    metric.loc,
2264                    @r###"
2265                    {
2266                      "sloc": 3.0,
2267                      "ploc": 3.0,
2268                      "lloc": 3.0,
2269                      "cloc": 3.0,
2270                      "blank": 0.0,
2271                      "sloc_average": 3.0,
2272                      "ploc_average": 3.0,
2273                      "lloc_average": 3.0,
2274                      "cloc_average": 3.0,
2275                      "blank_average": 0.0,
2276                      "sloc_min": 3.0,
2277                      "sloc_max": 3.0,
2278                      "cloc_min": 3.0,
2279                      "cloc_max": 3.0,
2280                      "ploc_min": 3.0,
2281                      "ploc_max": 3.0,
2282                      "lloc_min": 3.0,
2283                      "lloc_max": 3.0,
2284                      "blank_min": 0.0,
2285                      "blank_max": 0.0
2286                    }"###
2287                );
2288            },
2289        );
2290    }
2291
2292    #[test]
2293    fn rust_function_in_loop_lloc() {
2294        check_metrics::<RustParser>(
2295            "for (a, b) in c.iter().enumerate() {} // +1
2296             while (a, b) in c.iter().enumerate() {} // +1
2297             while let Some(a) = c.strip_prefix(\"hi\") {} // +1",
2298            "foo.rs",
2299            |metric| {
2300                // Spaces: 1
2301                insta::assert_json_snapshot!(
2302                    metric.loc,
2303                    @r###"
2304                    {
2305                      "sloc": 3.0,
2306                      "ploc": 3.0,
2307                      "lloc": 3.0,
2308                      "cloc": 3.0,
2309                      "blank": 0.0,
2310                      "sloc_average": 3.0,
2311                      "ploc_average": 3.0,
2312                      "lloc_average": 3.0,
2313                      "cloc_average": 3.0,
2314                      "blank_average": 0.0,
2315                      "sloc_min": 3.0,
2316                      "sloc_max": 3.0,
2317                      "cloc_min": 3.0,
2318                      "cloc_max": 3.0,
2319                      "ploc_min": 3.0,
2320                      "ploc_max": 3.0,
2321                      "lloc_min": 3.0,
2322                      "lloc_max": 3.0,
2323                      "blank_min": 0.0,
2324                      "blank_max": 0.0
2325                    }"###
2326                );
2327            },
2328        );
2329    }
2330
2331    #[test]
2332    fn rust_function_in_if_lloc() {
2333        check_metrics::<RustParser>(
2334            "if foo() {} // +1
2335             if let Some(a) = foo() {} // +1",
2336            "foo.rs",
2337            |metric| {
2338                // Spaces: 1
2339                insta::assert_json_snapshot!(
2340                    metric.loc,
2341                    @r###"
2342                    {
2343                      "sloc": 2.0,
2344                      "ploc": 2.0,
2345                      "lloc": 2.0,
2346                      "cloc": 2.0,
2347                      "blank": 0.0,
2348                      "sloc_average": 2.0,
2349                      "ploc_average": 2.0,
2350                      "lloc_average": 2.0,
2351                      "cloc_average": 2.0,
2352                      "blank_average": 0.0,
2353                      "sloc_min": 2.0,
2354                      "sloc_max": 2.0,
2355                      "cloc_min": 2.0,
2356                      "cloc_max": 2.0,
2357                      "ploc_min": 2.0,
2358                      "ploc_max": 2.0,
2359                      "lloc_min": 2.0,
2360                      "lloc_max": 2.0,
2361                      "blank_min": 0.0,
2362                      "blank_max": 0.0
2363                    }"###
2364                );
2365            },
2366        );
2367    }
2368
2369    #[test]
2370    fn rust_function_in_return_lloc() {
2371        check_metrics::<RustParser>(
2372            "return foo();
2373             await foo();",
2374            "foo.rs",
2375            |metric| {
2376                // Spaces: 1
2377                insta::assert_json_snapshot!(
2378                    metric.loc,
2379                    @r###"
2380                    {
2381                      "sloc": 2.0,
2382                      "ploc": 2.0,
2383                      "lloc": 2.0,
2384                      "cloc": 0.0,
2385                      "blank": 0.0,
2386                      "sloc_average": 2.0,
2387                      "ploc_average": 2.0,
2388                      "lloc_average": 2.0,
2389                      "cloc_average": 0.0,
2390                      "blank_average": 0.0,
2391                      "sloc_min": 2.0,
2392                      "sloc_max": 2.0,
2393                      "cloc_min": 0.0,
2394                      "cloc_max": 0.0,
2395                      "ploc_min": 2.0,
2396                      "ploc_max": 2.0,
2397                      "lloc_min": 2.0,
2398                      "lloc_max": 2.0,
2399                      "blank_min": 0.0,
2400                      "blank_max": 0.0
2401                    }"###
2402                );
2403            },
2404        );
2405    }
2406
2407    #[test]
2408    fn rust_closure_expression_lloc() {
2409        check_metrics::<RustParser>(
2410            "let a = |i: i32| -> i32 { i + 1 }; // +1
2411             a(42); // +1
2412             k(b.iter().map(|n| n.parse.ok().unwrap_or(42))); // +1",
2413            "foo.rs",
2414            |metric| {
2415                // Spaces: 3
2416                insta::assert_json_snapshot!(
2417                    metric.loc,
2418                    @r###"
2419                    {
2420                      "sloc": 3.0,
2421                      "ploc": 3.0,
2422                      "lloc": 3.0,
2423                      "cloc": 3.0,
2424                      "blank": 0.0,
2425                      "sloc_average": 1.0,
2426                      "ploc_average": 1.0,
2427                      "lloc_average": 1.0,
2428                      "cloc_average": 1.0,
2429                      "blank_average": 0.0,
2430                      "sloc_min": 1.0,
2431                      "sloc_max": 1.0,
2432                      "cloc_min": 0.0,
2433                      "cloc_max": 0.0,
2434                      "ploc_min": 1.0,
2435                      "ploc_max": 1.0,
2436                      "lloc_min": 0.0,
2437                      "lloc_max": 0.0,
2438                      "blank_min": 0.0,
2439                      "blank_max": 0.0
2440                    }"###
2441                );
2442            },
2443        );
2444    }
2445
2446    #[test]
2447    fn python_general_loc() {
2448        check_metrics::<PythonParser>(
2449            "def func(a,
2450                      b,
2451                      c):
2452                 print(a)
2453                 print(b)
2454                 print(c)",
2455            "foo.py",
2456            |metric| {
2457                // Spaces: 2
2458                insta::assert_json_snapshot!(
2459                    metric.loc,
2460                    @r#"
2461                {
2462                  "sloc": 6.0,
2463                  "ploc": 6.0,
2464                  "lloc": 3.0,
2465                  "cloc": 0.0,
2466                  "blank": 0.0,
2467                  "sloc_average": 3.0,
2468                  "ploc_average": 3.0,
2469                  "lloc_average": 1.5,
2470                  "cloc_average": 0.0,
2471                  "blank_average": 0.0,
2472                  "sloc_min": 3.0,
2473                  "sloc_max": 3.0,
2474                  "cloc_min": 0.0,
2475                  "cloc_max": 0.0,
2476                  "ploc_min": 3.0,
2477                  "ploc_max": 3.0,
2478                  "lloc_min": 0.0,
2479                  "lloc_max": 0.0,
2480                  "blank_min": 0.0,
2481                  "blank_max": 0.0
2482                }
2483                "#
2484                );
2485            },
2486        );
2487    }
2488
2489    #[test]
2490    fn python_real_loc() {
2491        check_metrics::<PythonParser>(
2492            "def web_socket_transfer_data(request):
2493                while True:
2494                    line = request.ws_stream.receive_message()
2495                    if line is None:
2496                        return
2497                    code, reason = line.split(' ', 1)
2498                    if code is None or reason is None:
2499                        return
2500                    request.ws_stream.close_connection(int(code), reason)
2501                    # close_connection() initiates closing handshake. It validates code
2502                    # and reason. If you want to send a broken close frame for a test,
2503                    # following code will be useful.
2504                    # > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
2505                    # > request.connection.write(stream.create_close_frame(data))
2506                    # > # Suppress to re-respond client responding close frame.
2507                    # > raise Exception(\"customized server initiated closing handshake\")",
2508            "foo.py",
2509            |metric| {
2510                // Spaces: 2
2511                insta::assert_json_snapshot!(
2512                    metric.loc,
2513                    @r#"
2514                {
2515                  "sloc": 16.0,
2516                  "ploc": 9.0,
2517                  "lloc": 8.0,
2518                  "cloc": 7.0,
2519                  "blank": 0.0,
2520                  "sloc_average": 8.0,
2521                  "ploc_average": 4.5,
2522                  "lloc_average": 4.0,
2523                  "cloc_average": 3.5,
2524                  "blank_average": 0.0,
2525                  "sloc_min": 1.0,
2526                  "sloc_max": 1.0,
2527                  "cloc_min": 0.0,
2528                  "cloc_max": 0.0,
2529                  "ploc_min": 1.0,
2530                  "ploc_max": 1.0,
2531                  "lloc_min": 0.0,
2532                  "lloc_max": 0.0,
2533                  "blank_min": 0.0,
2534                  "blank_max": 0.0
2535                }
2536                "#
2537                );
2538            },
2539        );
2540    }
2541
2542    #[test]
2543    fn javascript_real_loc() {
2544        check_metrics::<JavascriptParser>(
2545            "assert.throws(Test262Error, function() {
2546               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
2547                 return;
2548               }
2549             });",
2550            "foo.js",
2551            |metric| {
2552                // Spaces: 2
2553                insta::assert_json_snapshot!(
2554                    metric.loc,
2555                    @r#"
2556                {
2557                  "sloc": 5.0,
2558                  "ploc": 5.0,
2559                  "lloc": 3.0,
2560                  "cloc": 0.0,
2561                  "blank": 0.0,
2562                  "sloc_average": 5.0,
2563                  "ploc_average": 5.0,
2564                  "lloc_average": 3.0,
2565                  "cloc_average": 0.0,
2566                  "blank_average": 0.0,
2567                  "sloc_min": 5.0,
2568                  "sloc_max": 5.0,
2569                  "cloc_min": 0.0,
2570                  "cloc_max": 0.0,
2571                  "ploc_min": 5.0,
2572                  "ploc_max": 5.0,
2573                  "lloc_min": 3.0,
2574                  "lloc_max": 3.0,
2575                  "blank_min": 0.0,
2576                  "blank_max": 0.0
2577                }
2578                "#
2579                );
2580            },
2581        );
2582    }
2583
2584    #[test]
2585    fn mozjs_real_loc() {
2586        check_metrics::<MozjsParser>(
2587            "assert.throws(Test262Error, function() {
2588               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
2589                 return;
2590               }
2591             });",
2592            "foo.js",
2593            |metric| {
2594                // Spaces: 2
2595                insta::assert_json_snapshot!(
2596                    metric.loc,
2597                    @r###"
2598                    {
2599                      "sloc": 5.0,
2600                      "ploc": 5.0,
2601                      "lloc": 6.0,
2602                      "cloc": 0.0,
2603                      "blank": 0.0,
2604                      "sloc_average": 2.5,
2605                      "ploc_average": 2.5,
2606                      "lloc_average": 3.0,
2607                      "cloc_average": 0.0,
2608                      "blank_average": 0.0,
2609                      "sloc_min": 5.0,
2610                      "sloc_max": 5.0,
2611                      "cloc_min": 0.0,
2612                      "cloc_max": 0.0,
2613                      "ploc_min": 5.0,
2614                      "ploc_max": 5.0,
2615                      "lloc_min": 5.0,
2616                      "lloc_max": 5.0,
2617                      "blank_min": 0.0,
2618                      "blank_max": 0.0
2619                    }"###
2620                );
2621            },
2622        );
2623    }
2624
2625    #[test]
2626    fn cpp_namespace_loc() {
2627        check_metrics::<CppParser>(
2628            "namespace mozilla::dom::quota {} // namespace mozilla::dom::quota",
2629            "foo.cpp",
2630            |metric| {
2631                // Spaces: 2
2632                insta::assert_json_snapshot!(
2633                    metric.loc,
2634                    @r###"
2635                    {
2636                      "sloc": 1.0,
2637                      "ploc": 1.0,
2638                      "lloc": 0.0,
2639                      "cloc": 1.0,
2640                      "blank": 0.0,
2641                      "sloc_average": 0.5,
2642                      "ploc_average": 0.5,
2643                      "lloc_average": 0.0,
2644                      "cloc_average": 0.5,
2645                      "blank_average": 0.0,
2646                      "sloc_min": 1.0,
2647                      "sloc_max": 1.0,
2648                      "cloc_min": 0.0,
2649                      "cloc_max": 0.0,
2650                      "ploc_min": 1.0,
2651                      "ploc_max": 1.0,
2652                      "lloc_min": 0.0,
2653                      "lloc_max": 0.0,
2654                      "blank_min": 0.0,
2655                      "blank_max": 0.0
2656                    }"###
2657                );
2658            },
2659        );
2660    }
2661
2662    #[test]
2663    fn java_comments() {
2664        check_metrics::<JavaParser>(
2665            "for (int i = 0; i < 100; i++) { \
2666               // Print hello
2667               System.out.println(\"hello\"); \
2668               // Print world
2669               System.out.println(\"hello\"); \
2670             }",
2671            "foo.java",
2672            |metric| {
2673                // Spaces: 1
2674                insta::assert_json_snapshot!(
2675                    metric.loc,
2676                    @r###"
2677                    {
2678                      "sloc": 3.0,
2679                      "ploc": 3.0,
2680                      "lloc": 3.0,
2681                      "cloc": 2.0,
2682                      "blank": 0.0,
2683                      "sloc_average": 3.0,
2684                      "ploc_average": 3.0,
2685                      "lloc_average": 3.0,
2686                      "cloc_average": 2.0,
2687                      "blank_average": 0.0,
2688                      "sloc_min": 3.0,
2689                      "sloc_max": 3.0,
2690                      "cloc_min": 2.0,
2691                      "cloc_max": 2.0,
2692                      "ploc_min": 3.0,
2693                      "ploc_max": 3.0,
2694                      "lloc_min": 3.0,
2695                      "lloc_max": 3.0,
2696                      "blank_min": 0.0,
2697                      "blank_max": 0.0
2698                    }"###
2699                );
2700            },
2701        );
2702    }
2703
2704    #[test]
2705    fn java_blank() {
2706        check_metrics::<JavaParser>(
2707            "int x = 1;
2708
2709
2710            int y = 2;",
2711            "foo.java",
2712            |metric| {
2713                // Spaces: 1
2714                insta::assert_json_snapshot!(
2715                    metric.loc,
2716                    @r###"
2717                    {
2718                      "sloc": 4.0,
2719                      "ploc": 2.0,
2720                      "lloc": 2.0,
2721                      "cloc": 0.0,
2722                      "blank": 2.0,
2723                      "sloc_average": 4.0,
2724                      "ploc_average": 2.0,
2725                      "lloc_average": 2.0,
2726                      "cloc_average": 0.0,
2727                      "blank_average": 2.0,
2728                      "sloc_min": 4.0,
2729                      "sloc_max": 4.0,
2730                      "cloc_min": 0.0,
2731                      "cloc_max": 0.0,
2732                      "ploc_min": 2.0,
2733                      "ploc_max": 2.0,
2734                      "lloc_min": 2.0,
2735                      "lloc_max": 2.0,
2736                      "blank_min": 2.0,
2737                      "blank_max": 2.0
2738                    }"###
2739                );
2740            },
2741        );
2742    }
2743
2744    #[test]
2745    fn java_sloc() {
2746        check_metrics::<JavaParser>(
2747            "for (int i = 0; i < 100; i++) {
2748               System.out.println(i);
2749             }",
2750            "foo.java",
2751            |metric| {
2752                // Spaces: 1
2753                insta::assert_json_snapshot!(
2754                    metric.loc,
2755                    @r###"
2756                    {
2757                      "sloc": 3.0,
2758                      "ploc": 3.0,
2759                      "lloc": 2.0,
2760                      "cloc": 0.0,
2761                      "blank": 0.0,
2762                      "sloc_average": 3.0,
2763                      "ploc_average": 3.0,
2764                      "lloc_average": 2.0,
2765                      "cloc_average": 0.0,
2766                      "blank_average": 0.0,
2767                      "sloc_min": 3.0,
2768                      "sloc_max": 3.0,
2769                      "cloc_min": 0.0,
2770                      "cloc_max": 0.0,
2771                      "ploc_min": 3.0,
2772                      "ploc_max": 3.0,
2773                      "lloc_min": 2.0,
2774                      "lloc_max": 2.0,
2775                      "blank_min": 0.0,
2776                      "blank_max": 0.0
2777                    }"###
2778                );
2779            },
2780        );
2781    }
2782
2783    #[test]
2784    fn java_module_sloc() {
2785        check_metrics::<JavaParser>(
2786            "module helloworld{
2787              exports com.test;
2788            }",
2789            "foo.java",
2790            |metric| {
2791                // Spaces: 1
2792                insta::assert_json_snapshot!(
2793                    metric.loc,
2794                    @r###"
2795                    {
2796                      "sloc": 3.0,
2797                      "ploc": 3.0,
2798                      "lloc": 0.0,
2799                      "cloc": 0.0,
2800                      "blank": 0.0,
2801                      "sloc_average": 3.0,
2802                      "ploc_average": 3.0,
2803                      "lloc_average": 0.0,
2804                      "cloc_average": 0.0,
2805                      "blank_average": 0.0,
2806                      "sloc_min": 3.0,
2807                      "sloc_max": 3.0,
2808                      "cloc_min": 0.0,
2809                      "cloc_max": 0.0,
2810                      "ploc_min": 3.0,
2811                      "ploc_max": 3.0,
2812                      "lloc_min": 0.0,
2813                      "lloc_max": 0.0,
2814                      "blank_min": 0.0,
2815                      "blank_max": 0.0
2816                    }"###
2817                );
2818            },
2819        );
2820    }
2821
2822    #[test]
2823    fn java_single_ploc() {
2824        check_metrics::<JavaParser>("int x = 1;", "foo.java", |metric| {
2825            // Spaces: 1
2826            insta::assert_json_snapshot!(
2827                metric.loc,
2828                @r###"
2829                    {
2830                      "sloc": 1.0,
2831                      "ploc": 1.0,
2832                      "lloc": 1.0,
2833                      "cloc": 0.0,
2834                      "blank": 0.0,
2835                      "sloc_average": 1.0,
2836                      "ploc_average": 1.0,
2837                      "lloc_average": 1.0,
2838                      "cloc_average": 0.0,
2839                      "blank_average": 0.0,
2840                      "sloc_min": 1.0,
2841                      "sloc_max": 1.0,
2842                      "cloc_min": 0.0,
2843                      "cloc_max": 0.0,
2844                      "ploc_min": 1.0,
2845                      "ploc_max": 1.0,
2846                      "lloc_min": 1.0,
2847                      "lloc_max": 1.0,
2848                      "blank_min": 0.0,
2849                      "blank_max": 0.0
2850                    }"###
2851            );
2852        });
2853    }
2854
2855    #[test]
2856    fn java_simple_ploc() {
2857        check_metrics::<JavaParser>(
2858            "for (int i = 0; i < 100; i = i++) {
2859               System.out.println(i);
2860             }",
2861            "foo.java",
2862            |metric| {
2863                // Spaces: 1
2864                insta::assert_json_snapshot!(
2865                    metric.loc,
2866                    @r###"
2867                    {
2868                      "sloc": 3.0,
2869                      "ploc": 3.0,
2870                      "lloc": 2.0,
2871                      "cloc": 0.0,
2872                      "blank": 0.0,
2873                      "sloc_average": 3.0,
2874                      "ploc_average": 3.0,
2875                      "lloc_average": 2.0,
2876                      "cloc_average": 0.0,
2877                      "blank_average": 0.0,
2878                      "sloc_min": 3.0,
2879                      "sloc_max": 3.0,
2880                      "cloc_min": 0.0,
2881                      "cloc_max": 0.0,
2882                      "ploc_min": 3.0,
2883                      "ploc_max": 3.0,
2884                      "lloc_min": 2.0,
2885                      "lloc_max": 2.0,
2886                      "blank_min": 0.0,
2887                      "blank_max": 0.0
2888                    }"###
2889                );
2890            },
2891        );
2892    }
2893
2894    #[test]
2895    fn java_multi_ploc() {
2896        check_metrics::<JavaParser>(
2897            "int x = 1;
2898            for (int i = 0; i < 100; i++) {
2899               System.out.println(i);
2900             }",
2901            "foo.java",
2902            |metric| {
2903                // Spaces: 1
2904                insta::assert_json_snapshot!(
2905                    metric.loc,
2906                    @r###"
2907                    {
2908                      "sloc": 4.0,
2909                      "ploc": 4.0,
2910                      "lloc": 3.0,
2911                      "cloc": 0.0,
2912                      "blank": 0.0,
2913                      "sloc_average": 4.0,
2914                      "ploc_average": 4.0,
2915                      "lloc_average": 3.0,
2916                      "cloc_average": 0.0,
2917                      "blank_average": 0.0,
2918                      "sloc_min": 4.0,
2919                      "sloc_max": 4.0,
2920                      "cloc_min": 0.0,
2921                      "cloc_max": 0.0,
2922                      "ploc_min": 4.0,
2923                      "ploc_max": 4.0,
2924                      "lloc_min": 3.0,
2925                      "lloc_max": 3.0,
2926                      "blank_min": 0.0,
2927                      "blank_max": 0.0
2928                    }"###
2929                );
2930            },
2931        );
2932    }
2933
2934    #[test]
2935    fn java_single_statement_lloc() {
2936        check_metrics::<JavaParser>("int max = 10;", "foo.java", |metric| {
2937            // Spaces: 1
2938            insta::assert_json_snapshot!(
2939                metric.loc,
2940                @r###"
2941                    {
2942                      "sloc": 1.0,
2943                      "ploc": 1.0,
2944                      "lloc": 1.0,
2945                      "cloc": 0.0,
2946                      "blank": 0.0,
2947                      "sloc_average": 1.0,
2948                      "ploc_average": 1.0,
2949                      "lloc_average": 1.0,
2950                      "cloc_average": 0.0,
2951                      "blank_average": 0.0,
2952                      "sloc_min": 1.0,
2953                      "sloc_max": 1.0,
2954                      "cloc_min": 0.0,
2955                      "cloc_max": 0.0,
2956                      "ploc_min": 1.0,
2957                      "ploc_max": 1.0,
2958                      "lloc_min": 1.0,
2959                      "lloc_max": 1.0,
2960                      "blank_min": 0.0,
2961                      "blank_max": 0.0
2962                    }"###
2963            );
2964        });
2965    }
2966
2967    #[test]
2968    fn java_for_lloc() {
2969        check_metrics::<JavaParser>(
2970            "for (int i = 0; i < 100; i++) { // + 1
2971               System.out.println(i); // + 1
2972             }",
2973            "foo.java",
2974            |metric| {
2975                // Spaces: 1
2976                insta::assert_json_snapshot!(
2977                    metric.loc,
2978                    @r###"
2979                    {
2980                      "sloc": 3.0,
2981                      "ploc": 3.0,
2982                      "lloc": 2.0,
2983                      "cloc": 2.0,
2984                      "blank": 0.0,
2985                      "sloc_average": 3.0,
2986                      "ploc_average": 3.0,
2987                      "lloc_average": 2.0,
2988                      "cloc_average": 2.0,
2989                      "blank_average": 0.0,
2990                      "sloc_min": 3.0,
2991                      "sloc_max": 3.0,
2992                      "cloc_min": 2.0,
2993                      "cloc_max": 2.0,
2994                      "ploc_min": 3.0,
2995                      "ploc_max": 3.0,
2996                      "lloc_min": 2.0,
2997                      "lloc_max": 2.0,
2998                      "blank_min": 0.0,
2999                      "blank_max": 0.0
3000                    }"###
3001                );
3002            },
3003        );
3004    }
3005
3006    #[test]
3007    fn java_foreach_lloc() {
3008        check_metrics::<JavaParser>(
3009            "
3010            int arr[]={12,13,14,44}; // +1
3011            for (int i:arr) { // +1
3012               System.out.println(i); // +1
3013             }",
3014            "foo.java",
3015            |metric| {
3016                // Spaces: 1
3017                insta::assert_json_snapshot!(
3018                    metric.loc,
3019                    @r###"
3020                    {
3021                      "sloc": 4.0,
3022                      "ploc": 4.0,
3023                      "lloc": 3.0,
3024                      "cloc": 3.0,
3025                      "blank": 0.0,
3026                      "sloc_average": 4.0,
3027                      "ploc_average": 4.0,
3028                      "lloc_average": 3.0,
3029                      "cloc_average": 3.0,
3030                      "blank_average": 0.0,
3031                      "sloc_min": 4.0,
3032                      "sloc_max": 4.0,
3033                      "cloc_min": 3.0,
3034                      "cloc_max": 3.0,
3035                      "ploc_min": 4.0,
3036                      "ploc_max": 4.0,
3037                      "lloc_min": 3.0,
3038                      "lloc_max": 3.0,
3039                      "blank_min": 0.0,
3040                      "blank_max": 0.0
3041                    }"###
3042                );
3043            },
3044        );
3045    }
3046
3047    #[test]
3048    fn java_while_lloc() {
3049        check_metrics::<JavaParser>(
3050            "
3051            int i=0; // +1
3052            while(i < 10) { // +1
3053                i++; // +1
3054                System.out.println(i); // +1
3055             }",
3056            "foo.java",
3057            |metric| {
3058                // Spaces: 1
3059                insta::assert_json_snapshot!(
3060                    metric.loc,
3061                    @r###"
3062                    {
3063                      "sloc": 5.0,
3064                      "ploc": 5.0,
3065                      "lloc": 4.0,
3066                      "cloc": 4.0,
3067                      "blank": 0.0,
3068                      "sloc_average": 5.0,
3069                      "ploc_average": 5.0,
3070                      "lloc_average": 4.0,
3071                      "cloc_average": 4.0,
3072                      "blank_average": 0.0,
3073                      "sloc_min": 5.0,
3074                      "sloc_max": 5.0,
3075                      "cloc_min": 4.0,
3076                      "cloc_max": 4.0,
3077                      "ploc_min": 5.0,
3078                      "ploc_max": 5.0,
3079                      "lloc_min": 4.0,
3080                      "lloc_max": 4.0,
3081                      "blank_min": 0.0,
3082                      "blank_max": 0.0
3083                    }"###
3084                );
3085            },
3086        );
3087    }
3088
3089    #[test]
3090    fn java_do_while_lloc() {
3091        check_metrics::<JavaParser>(
3092            "
3093            int i=0; // +1
3094            do { // +1
3095                i++; // +1
3096                System.out.println(i); // +1
3097             } while(i < 10)",
3098            "foo.java",
3099            |metric| {
3100                // Spaces: 1
3101                insta::assert_json_snapshot!(
3102                    metric.loc,
3103                    @r###"
3104                    {
3105                      "sloc": 5.0,
3106                      "ploc": 5.0,
3107                      "lloc": 4.0,
3108                      "cloc": 4.0,
3109                      "blank": 0.0,
3110                      "sloc_average": 5.0,
3111                      "ploc_average": 5.0,
3112                      "lloc_average": 4.0,
3113                      "cloc_average": 4.0,
3114                      "blank_average": 0.0,
3115                      "sloc_min": 5.0,
3116                      "sloc_max": 5.0,
3117                      "cloc_min": 4.0,
3118                      "cloc_max": 4.0,
3119                      "ploc_min": 5.0,
3120                      "ploc_max": 5.0,
3121                      "lloc_min": 4.0,
3122                      "lloc_max": 4.0,
3123                      "blank_min": 0.0,
3124                      "blank_max": 0.0
3125                    }"###
3126                );
3127            },
3128        );
3129    }
3130
3131    #[test]
3132    fn java_switch_lloc() {
3133        check_metrics::<JavaParser>(
3134            "switch(grade) { // +1
3135                case 'A' :
3136                   System.out.println(\"Pass with distinction\"); // +1
3137                   break; // +1
3138                case 'B' :
3139                case 'C' :
3140                   System.out.println(\"Pass\"); // +1
3141                   break; // +1
3142                case 'D' :
3143                   System.out.println(\"At risk\"); // +1
3144                case 'F' :
3145                   System.out.println(\"Fail\"); // +1
3146                   break; // +1
3147                default :
3148                   System.out.println(\"Invalid grade\"); // +1
3149             }",
3150            "foo.java",
3151            |metric| {
3152                // Spaces: 1
3153                insta::assert_json_snapshot!(
3154                    metric.loc,
3155                    @r###"
3156                    {
3157                      "sloc": 16.0,
3158                      "ploc": 16.0,
3159                      "lloc": 9.0,
3160                      "cloc": 9.0,
3161                      "blank": 0.0,
3162                      "sloc_average": 16.0,
3163                      "ploc_average": 16.0,
3164                      "lloc_average": 9.0,
3165                      "cloc_average": 9.0,
3166                      "blank_average": 0.0,
3167                      "sloc_min": 16.0,
3168                      "sloc_max": 16.0,
3169                      "cloc_min": 9.0,
3170                      "cloc_max": 9.0,
3171                      "ploc_min": 16.0,
3172                      "ploc_max": 16.0,
3173                      "lloc_min": 9.0,
3174                      "lloc_max": 9.0,
3175                      "blank_min": 0.0,
3176                      "blank_max": 0.0
3177                    }"###
3178                );
3179            },
3180        );
3181    }
3182
3183    #[test]
3184    fn java_continue_lloc() {
3185        check_metrics::<JavaParser>(
3186            "int max = 10; // +1
3187
3188            for (int i = 0; i < max; i++) { // +1
3189                if(i % 2 == 0) { continue;} + 2
3190                System.out.println(i); // +1
3191             }",
3192            "foo.java",
3193            |metric| {
3194                // Spaces: 1
3195                insta::assert_json_snapshot!(
3196                    metric.loc,
3197                    @r###"
3198                    {
3199                      "sloc": 6.0,
3200                      "ploc": 5.0,
3201                      "lloc": 5.0,
3202                      "cloc": 3.0,
3203                      "blank": 1.0,
3204                      "sloc_average": 6.0,
3205                      "ploc_average": 5.0,
3206                      "lloc_average": 5.0,
3207                      "cloc_average": 3.0,
3208                      "blank_average": 1.0,
3209                      "sloc_min": 6.0,
3210                      "sloc_max": 6.0,
3211                      "cloc_min": 3.0,
3212                      "cloc_max": 3.0,
3213                      "ploc_min": 5.0,
3214                      "ploc_max": 5.0,
3215                      "lloc_min": 5.0,
3216                      "lloc_max": 5.0,
3217                      "blank_min": 1.0,
3218                      "blank_max": 1.0
3219                    }"###
3220                );
3221            },
3222        );
3223    }
3224
3225    #[test]
3226    fn java_try_lloc() {
3227        check_metrics::<JavaParser>(
3228            "try { // +1
3229                int[] myNumbers = {1, 2, 3}; // +1
3230                System.out.println(myNumbers[10]); // +1
3231              } catch (Exception e) {
3232                System.out.println(e.getMessage()); // +1
3233                throw e; // +1
3234              }",
3235            "foo.java",
3236            |metric| {
3237                // Spaces: 1
3238                insta::assert_json_snapshot!(
3239                    metric.loc,
3240                    @r###"
3241                    {
3242                      "sloc": 7.0,
3243                      "ploc": 7.0,
3244                      "lloc": 5.0,
3245                      "cloc": 5.0,
3246                      "blank": 0.0,
3247                      "sloc_average": 7.0,
3248                      "ploc_average": 7.0,
3249                      "lloc_average": 5.0,
3250                      "cloc_average": 5.0,
3251                      "blank_average": 0.0,
3252                      "sloc_min": 7.0,
3253                      "sloc_max": 7.0,
3254                      "cloc_min": 5.0,
3255                      "cloc_max": 5.0,
3256                      "ploc_min": 7.0,
3257                      "ploc_max": 7.0,
3258                      "lloc_min": 5.0,
3259                      "lloc_max": 5.0,
3260                      "blank_min": 0.0,
3261                      "blank_max": 0.0
3262                    }"###
3263                );
3264            },
3265        );
3266    }
3267
3268    #[test]
3269    fn java_class_loc() {
3270        check_metrics::<JavaParser>(
3271            "
3272            public class Person {
3273              private String name;
3274              public Person(String name){
3275                this.name = name; // +1
3276              }
3277              public String getName() {
3278                return name; // +1
3279              }
3280            }",
3281            "foo.java",
3282            |metric| {
3283                // Spaces: 4
3284                insta::assert_json_snapshot!(
3285                    metric.loc,
3286                    @r###"
3287                    {
3288                      "sloc": 9.0,
3289                      "ploc": 9.0,
3290                      "lloc": 2.0,
3291                      "cloc": 2.0,
3292                      "blank": 0.0,
3293                      "sloc_average": 2.25,
3294                      "ploc_average": 2.25,
3295                      "lloc_average": 0.5,
3296                      "cloc_average": 0.5,
3297                      "blank_average": 0.0,
3298                      "sloc_min": 9.0,
3299                      "sloc_max": 9.0,
3300                      "cloc_min": 2.0,
3301                      "cloc_max": 2.0,
3302                      "ploc_min": 9.0,
3303                      "ploc_max": 9.0,
3304                      "lloc_min": 2.0,
3305                      "lloc_max": 2.0,
3306                      "blank_min": 0.0,
3307                      "blank_max": 0.0
3308                    }"###
3309                );
3310            },
3311        );
3312    }
3313
3314    #[test]
3315    fn java_expressions_lloc() {
3316        check_metrics::<JavaParser>(
3317            "int x = 10;                                                            // +1 local var declaration
3318            x=+89;                                                                  // +1 expression statement
3319            int y = x * 2;                                                          // +1 local var declaration
3320            IntFunction double = (n) -> n*2;                                        // +1 local var declaration
3321            int y2 = double(x);                                                     // +1 local var declaration
3322            System.out.println(\"double \" + x + \" = \" + y2);                     // +1 expression statement
3323            String message = (x % 2) == 0 ? \"Evenly done.\" : \"Oddly done.\";     // +1 local var declaration
3324            Object done = (Runnable) () -> { System.out.println(\"Done!\"); };      // +2 local var declaration + expression statement
3325            String s = \"string\";                                                  // +1 local var declaration
3326            boolean isS = (s instanceof String);                                    // +1 local var declaration
3327            done.run();                                                             // +1 expression statement
3328            ",
3329            "foo.java",
3330            |metric| {
3331                // Spaces: 1
3332                insta::assert_json_snapshot!(
3333                    metric.loc,
3334                    @r###"
3335                    {
3336                      "sloc": 11.0,
3337                      "ploc": 11.0,
3338                      "lloc": 12.0,
3339                      "cloc": 11.0,
3340                      "blank": 0.0,
3341                      "sloc_average": 11.0,
3342                      "ploc_average": 11.0,
3343                      "lloc_average": 12.0,
3344                      "cloc_average": 11.0,
3345                      "blank_average": 0.0,
3346                      "sloc_min": 11.0,
3347                      "sloc_max": 11.0,
3348                      "cloc_min": 11.0,
3349                      "cloc_max": 11.0,
3350                      "ploc_min": 11.0,
3351                      "ploc_max": 11.0,
3352                      "lloc_min": 12.0,
3353                      "lloc_max": 12.0,
3354                      "blank_min": 0.0,
3355                      "blank_max": 0.0
3356                    }"###
3357                );
3358            },
3359        );
3360    }
3361
3362    #[test]
3363    fn java_statement_inline_loc() {
3364        check_metrics::<JavaParser>(
3365            "for (int i = 0; i < 100; i++) { System.out.println(\"hello\"); }",
3366            "foo.java",
3367            |metric| {
3368                // Spaces: 1
3369                insta::assert_json_snapshot!(
3370                    metric.loc,
3371                    @r###"
3372                    {
3373                      "sloc": 1.0,
3374                      "ploc": 1.0,
3375                      "lloc": 2.0,
3376                      "cloc": 0.0,
3377                      "blank": 0.0,
3378                      "sloc_average": 1.0,
3379                      "ploc_average": 1.0,
3380                      "lloc_average": 2.0,
3381                      "cloc_average": 0.0,
3382                      "blank_average": 0.0,
3383                      "sloc_min": 1.0,
3384                      "sloc_max": 1.0,
3385                      "cloc_min": 0.0,
3386                      "cloc_max": 0.0,
3387                      "ploc_min": 1.0,
3388                      "ploc_max": 1.0,
3389                      "lloc_min": 2.0,
3390                      "lloc_max": 2.0,
3391                      "blank_min": 0.0,
3392                      "blank_max": 0.0
3393                    }"###
3394                );
3395            },
3396        );
3397    }
3398
3399    #[test]
3400    fn java_general_loc() {
3401        check_metrics::<JavaParser>(
3402            "int max = 100;
3403
3404            /*
3405              Loop through and print
3406                from: 0
3407                to: max
3408            */
3409            for (int i = 0; i < max; i++) {
3410               // Print the value
3411               System.out.println(i);
3412             }",
3413            "foo.java",
3414            |metric| {
3415                // Spaces: 1
3416                insta::assert_json_snapshot!(
3417                    metric.loc,
3418                    @r###"
3419                    {
3420                      "sloc": 11.0,
3421                      "ploc": 4.0,
3422                      "lloc": 3.0,
3423                      "cloc": 6.0,
3424                      "blank": 1.0,
3425                      "sloc_average": 11.0,
3426                      "ploc_average": 4.0,
3427                      "lloc_average": 3.0,
3428                      "cloc_average": 6.0,
3429                      "blank_average": 1.0,
3430                      "sloc_min": 11.0,
3431                      "sloc_max": 11.0,
3432                      "cloc_min": 6.0,
3433                      "cloc_max": 6.0,
3434                      "ploc_min": 4.0,
3435                      "ploc_max": 4.0,
3436                      "lloc_min": 3.0,
3437                      "lloc_max": 3.0,
3438                      "blank_min": 1.0,
3439                      "blank_max": 1.0
3440                    }"###
3441                );
3442            },
3443        );
3444    }
3445
3446    #[test]
3447    fn java_main_class_loc() {
3448        check_metrics::<JavaParser>(
3449            "package com.company;
3450             /**
3451             * The HelloWorldApp class implements an application that
3452             * simply prints \"Hello World!\" to standard output.
3453             */
3454
3455            class HelloWorldApp {
3456              public void main(String[] args) {
3457                String message = args.length == 0 ? \"Hello empty world\" : \"Hello world\"; // +1 lloc : 1 var assignment
3458                System.out.println(message); // Display the string. +1 lloc
3459              }
3460            }",
3461            "foo.java",
3462            |metric| {
3463                // Spaces: 3
3464                insta::assert_json_snapshot!(
3465                    metric.loc,
3466                    @r###"
3467                    {
3468                      "sloc": 12.0,
3469                      "ploc": 7.0,
3470                      "lloc": 2.0,
3471                      "cloc": 6.0,
3472                      "blank": 1.0,
3473                      "sloc_average": 4.0,
3474                      "ploc_average": 2.3333333333333335,
3475                      "lloc_average": 0.6666666666666666,
3476                      "cloc_average": 2.0,
3477                      "blank_average": 0.3333333333333333,
3478                      "sloc_min": 6.0,
3479                      "sloc_max": 6.0,
3480                      "cloc_min": 2.0,
3481                      "cloc_max": 2.0,
3482                      "ploc_min": 6.0,
3483                      "ploc_max": 6.0,
3484                      "lloc_min": 2.0,
3485                      "lloc_max": 2.0,
3486                      "blank_min": 0.0,
3487                      "blank_max": 0.0
3488                    }"###
3489                );
3490            },
3491        );
3492    }
3493}