rust_code_analysis/metrics/
loc.rs

1use crate::checker::Checker;
2use fxhash::FxHashSet;
3use serde::ser::{SerializeStruct, Serializer};
4use serde::Serialize;
5use std::fmt;
6
7use crate::*;
8
9/// The `SLoc` metric suite.
10#[derive(Debug, Clone)]
11pub struct Sloc {
12    start: usize,
13    end: usize,
14    unit: bool,
15    sloc_min: usize,
16    sloc_max: usize,
17}
18
19impl Default for Sloc {
20    fn default() -> Self {
21        Self {
22            start: 0,
23            end: 0,
24            unit: false,
25            sloc_min: usize::MAX,
26            sloc_max: 0,
27        }
28    }
29}
30
31impl Sloc {
32    #[inline(always)]
33    pub fn sloc(&self) -> f64 {
34        // This metric counts the number of lines in a file
35        // The if construct is needed to count the line of code that represents
36        // the function signature in a function space
37        let sloc = if self.unit {
38            self.end - self.start
39        } else {
40            (self.end - self.start) + 1
41        };
42        sloc as f64
43    }
44
45    /// The `Sloc` metric minimum value.
46    #[inline(always)]
47    pub fn sloc_min(&self) -> f64 {
48        self.sloc_min as f64
49    }
50
51    /// The `Sloc` metric maximum value.
52    #[inline(always)]
53    pub fn sloc_max(&self) -> f64 {
54        self.sloc_max as f64
55    }
56
57    #[inline(always)]
58    pub fn merge(&mut self, other: &Sloc) {
59        self.sloc_min = self.sloc_min.min(other.sloc() as usize);
60        self.sloc_max = self.sloc_max.max(other.sloc() as usize);
61    }
62
63    #[inline(always)]
64    pub(crate) fn compute_minmax(&mut self) {
65        if self.sloc_min == usize::MAX {
66            self.sloc_min = self.sloc_min.min(self.sloc() as usize);
67            self.sloc_max = self.sloc_max.max(self.sloc() as usize);
68        }
69    }
70}
71
72/// The `PLoc` metric suite.
73#[derive(Debug, Clone)]
74pub struct Ploc {
75    lines: FxHashSet<usize>,
76    ploc_min: usize,
77    ploc_max: usize,
78}
79
80impl Default for Ploc {
81    fn default() -> Self {
82        Self {
83            lines: FxHashSet::default(),
84            ploc_min: usize::MAX,
85            ploc_max: 0,
86        }
87    }
88}
89
90impl Ploc {
91    #[inline(always)]
92    pub fn ploc(&self) -> f64 {
93        // This metric counts the number of instruction lines in a code
94        // https://en.wikipedia.org/wiki/Source_lines_of_code
95        self.lines.len() as f64
96    }
97
98    /// The `Ploc` metric minimum value.
99    #[inline(always)]
100    pub fn ploc_min(&self) -> f64 {
101        self.ploc_min as f64
102    }
103
104    /// The `Ploc` metric maximum value.
105    #[inline(always)]
106    pub fn ploc_max(&self) -> f64 {
107        self.ploc_max as f64
108    }
109
110    #[inline(always)]
111    pub fn merge(&mut self, other: &Ploc) {
112        // Merge ploc lines
113        for l in other.lines.iter() {
114            self.lines.insert(*l);
115        }
116
117        self.ploc_min = self.ploc_min.min(other.ploc() as usize);
118        self.ploc_max = self.ploc_max.max(other.ploc() as usize);
119    }
120
121    #[inline(always)]
122    pub(crate) fn compute_minmax(&mut self) {
123        if self.ploc_min == usize::MAX {
124            self.ploc_min = self.ploc_min.min(self.ploc() as usize);
125            self.ploc_max = self.ploc_max.max(self.ploc() as usize);
126        }
127    }
128}
129
130/// The `CLoc` metric suite.
131#[derive(Debug, Clone)]
132pub struct Cloc {
133    only_comment_lines: usize,
134    code_comment_lines: usize,
135    comment_line_end: Option<usize>,
136    cloc_min: usize,
137    cloc_max: usize,
138}
139
140impl Default for Cloc {
141    fn default() -> Self {
142        Self {
143            only_comment_lines: 0,
144            code_comment_lines: 0,
145            comment_line_end: Option::default(),
146            cloc_min: usize::MAX,
147            cloc_max: 0,
148        }
149    }
150}
151
152impl Cloc {
153    #[inline(always)]
154    pub fn cloc(&self) -> f64 {
155        // Comments are counted regardless of their placement
156        // https://en.wikipedia.org/wiki/Source_lines_of_code
157        (self.only_comment_lines + self.code_comment_lines) as f64
158    }
159
160    /// The `Ploc` metric minimum value.
161    #[inline(always)]
162    pub fn cloc_min(&self) -> f64 {
163        self.cloc_min as f64
164    }
165
166    /// The `Ploc` metric maximum value.
167    #[inline(always)]
168    pub fn cloc_max(&self) -> f64 {
169        self.cloc_max as f64
170    }
171
172    #[inline(always)]
173    pub fn merge(&mut self, other: &Cloc) {
174        // Merge cloc lines
175        self.only_comment_lines += other.only_comment_lines;
176        self.code_comment_lines += other.code_comment_lines;
177
178        self.cloc_min = self.cloc_min.min(other.cloc() as usize);
179        self.cloc_max = self.cloc_max.max(other.cloc() as usize);
180    }
181
182    #[inline(always)]
183    pub(crate) fn compute_minmax(&mut self) {
184        if self.cloc_min == usize::MAX {
185            self.cloc_min = self.cloc_min.min(self.cloc() as usize);
186            self.cloc_max = self.cloc_max.max(self.cloc() as usize);
187        }
188    }
189}
190
191/// The `LLoc` metric suite.
192#[derive(Debug, Clone)]
193pub struct Lloc {
194    logical_lines: usize,
195    lloc_min: usize,
196    lloc_max: usize,
197}
198
199impl Default for Lloc {
200    fn default() -> Self {
201        Self {
202            logical_lines: 0,
203            lloc_min: usize::MAX,
204            lloc_max: 0,
205        }
206    }
207}
208
209impl Lloc {
210    #[inline(always)]
211    pub fn lloc(&self) -> f64 {
212        // This metric counts the number of statements in a code
213        // https://en.wikipedia.org/wiki/Source_lines_of_code
214        self.logical_lines as f64
215    }
216
217    /// The `Lloc` metric minimum value.
218    #[inline(always)]
219    pub fn lloc_min(&self) -> f64 {
220        self.lloc_min as f64
221    }
222
223    /// The `Lloc` metric maximum value.
224    #[inline(always)]
225    pub fn lloc_max(&self) -> f64 {
226        self.lloc_max as f64
227    }
228
229    #[inline(always)]
230    pub fn merge(&mut self, other: &Lloc) {
231        // Merge lloc lines
232        self.logical_lines += other.logical_lines;
233        self.lloc_min = self.lloc_min.min(other.lloc() as usize);
234        self.lloc_max = self.lloc_max.max(other.lloc() as usize);
235    }
236
237    #[inline(always)]
238    pub(crate) fn compute_minmax(&mut self) {
239        if self.lloc_min == usize::MAX {
240            self.lloc_min = self.lloc_min.min(self.lloc() as usize);
241            self.lloc_max = self.lloc_max.max(self.lloc() as usize);
242        }
243    }
244}
245
246/// The `Loc` metric suite.
247#[derive(Debug, Clone)]
248pub struct Stats {
249    sloc: Sloc,
250    ploc: Ploc,
251    cloc: Cloc,
252    lloc: Lloc,
253    space_count: usize,
254    blank_min: usize,
255    blank_max: usize,
256}
257
258impl Default for Stats {
259    fn default() -> Self {
260        Self {
261            sloc: Sloc::default(),
262            ploc: Ploc::default(),
263            cloc: Cloc::default(),
264            lloc: Lloc::default(),
265            space_count: 1,
266            blank_min: usize::MAX,
267            blank_max: 0,
268        }
269    }
270}
271
272impl Serialize for Stats {
273    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274    where
275        S: Serializer,
276    {
277        let mut st = serializer.serialize_struct("loc", 20)?;
278        st.serialize_field("sloc", &self.sloc())?;
279        st.serialize_field("ploc", &self.ploc())?;
280        st.serialize_field("lloc", &self.lloc())?;
281        st.serialize_field("cloc", &self.cloc())?;
282        st.serialize_field("blank", &self.blank())?;
283        st.serialize_field("sloc_average", &self.sloc_average())?;
284        st.serialize_field("ploc_average", &self.ploc_average())?;
285        st.serialize_field("lloc_average", &self.lloc_average())?;
286        st.serialize_field("cloc_average", &self.cloc_average())?;
287        st.serialize_field("blank_average", &self.blank_average())?;
288        st.serialize_field("sloc_min", &self.sloc_min())?;
289        st.serialize_field("sloc_max", &self.sloc_max())?;
290        st.serialize_field("cloc_min", &self.cloc_min())?;
291        st.serialize_field("cloc_max", &self.cloc_max())?;
292        st.serialize_field("ploc_min", &self.ploc_min())?;
293        st.serialize_field("ploc_max", &self.ploc_max())?;
294        st.serialize_field("lloc_min", &self.lloc_min())?;
295        st.serialize_field("lloc_max", &self.lloc_max())?;
296        st.serialize_field("blank_min", &self.blank_min())?;
297        st.serialize_field("blank_max", &self.blank_max())?;
298        st.end()
299    }
300}
301
302impl fmt::Display for Stats {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        write!(
305            f,
306            "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: {}",
307            self.sloc(),
308            self.ploc(),
309            self.lloc(),
310            self.cloc(),
311            self.blank(),
312            self.sloc_average(),
313            self.ploc_average(),
314            self.lloc_average(),
315            self.cloc_average(),
316            self.blank_average(),
317            self.sloc_min(),
318            self.sloc_max(),
319            self.cloc_min(),
320            self.cloc_max(),
321            self.ploc_min(),
322            self.ploc_max(),
323            self.lloc_min(),
324            self.lloc_max(),
325            self.blank_min(),
326            self.blank_max(),
327        )
328    }
329}
330
331impl Stats {
332    /// Merges a second `Loc` metric suite into the first one
333    pub fn merge(&mut self, other: &Stats) {
334        self.sloc.merge(&other.sloc);
335        self.ploc.merge(&other.ploc);
336        self.cloc.merge(&other.cloc);
337        self.lloc.merge(&other.lloc);
338
339        // Count spaces
340        self.space_count += other.space_count;
341
342        // min and max
343
344        self.blank_min = self.blank_min.min(other.blank() as usize);
345        self.blank_max = self.blank_max.max(other.blank() as usize);
346    }
347
348    /// The `Sloc` metric.
349    ///
350    /// Counts the number of lines in a scope
351    #[inline(always)]
352    pub fn sloc(&self) -> f64 {
353        self.sloc.sloc()
354    }
355
356    /// The `Ploc` metric.
357    ///
358    /// Counts the number of instruction lines in a scope
359    #[inline(always)]
360    pub fn ploc(&self) -> f64 {
361        self.ploc.ploc()
362    }
363
364    /// The `Lloc` metric.
365    ///
366    /// Counts the number of statements in a scope
367    #[inline(always)]
368    pub fn lloc(&self) -> f64 {
369        self.lloc.lloc()
370    }
371
372    /// The `Cloc` metric.
373    ///
374    /// Counts the number of comments in a scope
375    #[inline(always)]
376    pub fn cloc(&self) -> f64 {
377        self.cloc.cloc()
378    }
379
380    /// The `Blank` metric.
381    ///
382    /// Counts the number of blank lines in a scope
383    #[inline(always)]
384    pub fn blank(&self) -> f64 {
385        self.sloc() - self.ploc() - self.cloc.only_comment_lines as f64
386    }
387
388    /// The `Sloc` metric average value.
389    ///
390    /// This value is computed dividing the `Sloc` value for the number of spaces
391    #[inline(always)]
392    pub fn sloc_average(&self) -> f64 {
393        self.sloc() / self.space_count as f64
394    }
395
396    /// The `Ploc` metric average value.
397    ///
398    /// This value is computed dividing the `Ploc` value for the number of spaces
399    #[inline(always)]
400    pub fn ploc_average(&self) -> f64 {
401        self.ploc() / self.space_count as f64
402    }
403
404    /// The `Lloc` metric average value.
405    ///
406    /// This value is computed dividing the `Lloc` value for the number of spaces
407    #[inline(always)]
408    pub fn lloc_average(&self) -> f64 {
409        self.lloc() / self.space_count as f64
410    }
411
412    /// The `Cloc` metric average value.
413    ///
414    /// This value is computed dividing the `Cloc` value for the number of spaces
415    #[inline(always)]
416    pub fn cloc_average(&self) -> f64 {
417        self.cloc() / self.space_count as f64
418    }
419
420    /// The `Blank` metric average value.
421    ///
422    /// This value is computed dividing the `Blank` value for the number of spaces
423    #[inline(always)]
424    pub fn blank_average(&self) -> f64 {
425        self.blank() / self.space_count as f64
426    }
427
428    /// The `Sloc` metric minimum value.
429    #[inline(always)]
430    pub fn sloc_min(&self) -> f64 {
431        self.sloc.sloc_min()
432    }
433
434    /// The `Sloc` metric maximum value.
435    #[inline(always)]
436    pub fn sloc_max(&self) -> f64 {
437        self.sloc.sloc_max()
438    }
439
440    /// The `Cloc` metric minimum value.
441    #[inline(always)]
442    pub fn cloc_min(&self) -> f64 {
443        self.cloc.cloc_min()
444    }
445
446    /// The `Cloc` metric maximum value.
447    #[inline(always)]
448    pub fn cloc_max(&self) -> f64 {
449        self.cloc.cloc_max()
450    }
451
452    /// The `Ploc` metric minimum value.
453    #[inline(always)]
454    pub fn ploc_min(&self) -> f64 {
455        self.ploc.ploc_min()
456    }
457
458    /// The `Ploc` metric maximum value.
459    #[inline(always)]
460    pub fn ploc_max(&self) -> f64 {
461        self.ploc.ploc_max()
462    }
463
464    /// The `Lloc` metric minimum value.
465    #[inline(always)]
466    pub fn lloc_min(&self) -> f64 {
467        self.lloc.lloc_min()
468    }
469
470    /// The `Lloc` metric maximum value.
471    #[inline(always)]
472    pub fn lloc_max(&self) -> f64 {
473        self.lloc.lloc_max()
474    }
475
476    /// The `Blank` metric minimum value.
477    #[inline(always)]
478    pub fn blank_min(&self) -> f64 {
479        self.blank_min as f64
480    }
481
482    /// The `Blank` metric maximum value.
483    #[inline(always)]
484    pub fn blank_max(&self) -> f64 {
485        self.blank_max as f64
486    }
487
488    #[inline(always)]
489    pub(crate) fn compute_minmax(&mut self) {
490        self.sloc.compute_minmax();
491        self.ploc.compute_minmax();
492        self.cloc.compute_minmax();
493        self.lloc.compute_minmax();
494
495        if self.blank_min == usize::MAX {
496            self.blank_min = self.blank_min.min(self.blank() as usize);
497            self.blank_max = self.blank_max.max(self.blank() as usize);
498        }
499    }
500}
501
502#[doc(hidden)]
503pub trait Loc
504where
505    Self: Checker,
506{
507    fn compute(_node: &Node, _stats: &mut Stats, _is_func_space: bool, _is_unit: bool) {}
508}
509
510#[inline(always)]
511fn init(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) -> (usize, usize) {
512    let start = node.object().start_position().row;
513    let end = node.object().end_position().row;
514
515    if is_func_space {
516        stats.sloc.start = start;
517        stats.sloc.end = end;
518        stats.sloc.unit = is_unit;
519    }
520    (start, end)
521}
522
523#[inline(always)]
524// Discriminates among the comments that are *after* a code line and
525// the ones that are on an independent line.
526// This difference is necessary in order to avoid having
527// a wrong count for the blank metric.
528fn add_cloc_lines(stats: &mut Stats, start: usize, end: usize) {
529    let comment_diff = end - start;
530    let is_comment_after_code_line = stats.ploc.lines.contains(&start);
531    if is_comment_after_code_line && comment_diff == 0 {
532        // A comment is *entirely* next to a code line
533        stats.cloc.code_comment_lines += 1;
534    } else if is_comment_after_code_line && comment_diff > 0 {
535        // A block comment that starts next to a code line and ends on
536        // independent lines.
537        stats.cloc.code_comment_lines += 1;
538        stats.cloc.only_comment_lines += comment_diff;
539    } else {
540        // A comment on an independent line AND
541        // a block comment on independent lines OR
542        // a comment *before* a code line
543        stats.cloc.only_comment_lines += (end - start) + 1;
544        // Save line end of a comment to check whether
545        // a comment *before* a code line is considered
546        stats.cloc.comment_line_end = Some(end);
547    }
548}
549
550#[inline(always)]
551// Detects the comments that are on a code line but *before* the code part.
552// This difference is necessary in order to avoid having
553// a wrong count for the blank metric.
554fn check_comment_ends_on_code_line(stats: &mut Stats, start_code_line: usize) {
555    if let Some(end) = stats.cloc.comment_line_end {
556        if end == start_code_line && !stats.ploc.lines.contains(&start_code_line) {
557            // Comment entirely *before* a code line
558            stats.cloc.only_comment_lines -= 1;
559            stats.cloc.code_comment_lines += 1;
560        }
561    }
562}
563
564impl Loc for PythonCode {
565    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
566        use Python::*;
567
568        let (start, end) = init(node, stats, is_func_space, is_unit);
569
570        match node.object().kind_id().into() {
571            DQUOTE | DQUOTE2 | Block | Module => {}
572            Comment => {
573                add_cloc_lines(stats, start, end);
574            }
575            String => {
576                let parent = node.object().parent().unwrap();
577                if let ExpressionStatement = parent.kind_id().into() {
578                    add_cloc_lines(stats, start, end);
579                } else if parent.start_position().row != start {
580                    check_comment_ends_on_code_line(stats, start);
581                    stats.ploc.lines.insert(start);
582                }
583            }
584            Statement
585            | SimpleStatements
586            | ImportStatement
587            | FutureImportStatement
588            | ImportFromStatement
589            | PrintStatement
590            | AssertStatement
591            | ReturnStatement
592            | DeleteStatement
593            | RaiseStatement
594            | PassStatement
595            | BreakStatement
596            | ContinueStatement
597            | IfStatement
598            | ForStatement
599            | WhileStatement
600            | TryStatement
601            | WithStatement
602            | GlobalStatement
603            | NonlocalStatement
604            | ExecStatement
605            | ExpressionStatement => {
606                stats.lloc.logical_lines += 1;
607            }
608            _ => {
609                check_comment_ends_on_code_line(stats, start);
610                stats.ploc.lines.insert(start);
611            }
612        }
613    }
614}
615
616impl Loc for MozjsCode {
617    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
618        use Mozjs::*;
619
620        let (start, end) = init(node, stats, is_func_space, is_unit);
621
622        match node.object().kind_id().into() {
623            String | DQUOTE | Program => {}
624            Comment => {
625                add_cloc_lines(stats, start, end);
626            }
627            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
628            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
629            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
630            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
631            | StatementIdentifier => {
632                stats.lloc.logical_lines += 1;
633            }
634            _ => {
635                check_comment_ends_on_code_line(stats, start);
636                stats.ploc.lines.insert(start);
637            }
638        }
639    }
640}
641
642impl Loc for JavascriptCode {
643    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
644        use Javascript::*;
645
646        let (start, end) = init(node, stats, is_func_space, is_unit);
647
648        match node.object().kind_id().into() {
649            String | DQUOTE | Program => {}
650            Comment => {
651                add_cloc_lines(stats, start, end);
652            }
653            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
654            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
655            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
656            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
657            | StatementIdentifier => {
658                stats.lloc.logical_lines += 1;
659            }
660            _ => {
661                check_comment_ends_on_code_line(stats, start);
662                stats.ploc.lines.insert(start);
663            }
664        }
665    }
666}
667
668impl Loc for TypescriptCode {
669    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
670        use Typescript::*;
671
672        let (start, end) = init(node, stats, is_func_space, is_unit);
673
674        match node.object().kind_id().into() {
675            String | DQUOTE | Program => {}
676            Comment => {
677                add_cloc_lines(stats, start, end);
678            }
679            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
680            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
681            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
682            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
683            | StatementIdentifier => {
684                stats.lloc.logical_lines += 1;
685            }
686            _ => {
687                check_comment_ends_on_code_line(stats, start);
688                stats.ploc.lines.insert(start);
689            }
690        }
691    }
692}
693
694impl Loc for TsxCode {
695    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
696        use Tsx::*;
697
698        let (start, end) = init(node, stats, is_func_space, is_unit);
699
700        match node.object().kind_id().into() {
701            String | DQUOTE | Program => {}
702            Comment => {
703                add_cloc_lines(stats, start, end);
704            }
705            ExpressionStatement | ExportStatement | ImportStatement | StatementBlock
706            | IfStatement | SwitchStatement | ForStatement | ForInStatement | WhileStatement
707            | DoStatement | TryStatement | WithStatement | BreakStatement | ContinueStatement
708            | DebuggerStatement | ReturnStatement | ThrowStatement | EmptyStatement
709            | StatementIdentifier => {
710                stats.lloc.logical_lines += 1;
711            }
712            _ => {
713                check_comment_ends_on_code_line(stats, start);
714                stats.ploc.lines.insert(start);
715            }
716        }
717    }
718}
719
720impl Loc for RustCode {
721    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
722        use Rust::*;
723
724        let (start, end) = init(node, stats, is_func_space, is_unit);
725
726        match node.object().kind_id().into() {
727            StringLiteral | RawStringLiteral | Block | SourceFile => {}
728            LineComment | BlockComment => {
729                add_cloc_lines(stats, start, end);
730            }
731            Statement
732            | EmptyStatement
733            | ExpressionStatement
734            | LetDeclaration
735            | AssignmentExpression
736            | CompoundAssignmentExpr => {
737                stats.lloc.logical_lines += 1;
738            }
739            _ => {
740                check_comment_ends_on_code_line(stats, start);
741                stats.ploc.lines.insert(start);
742            }
743        }
744    }
745}
746
747impl Loc for CppCode {
748    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
749        use Cpp::*;
750
751        let (start, end) = init(node, stats, is_func_space, is_unit);
752
753        match node.object().kind_id().into() {
754            RawStringLiteral | StringLiteral | DeclarationList | FieldDeclarationList
755            | TranslationUnit => {}
756            Comment => {
757                add_cloc_lines(stats, start, end);
758            }
759            WhileStatement | SwitchStatement | CaseStatement | IfStatement | ForStatement
760            | ReturnStatement | BreakStatement | ContinueStatement | GotoStatement
761            | ThrowStatement | TryStatement | ExpressionStatement | LabeledStatement
762            | StatementIdentifier => {
763                stats.lloc.logical_lines += 1;
764            }
765            Declaration => {
766                if count_specific_ancestors!(
767                    node,
768                    WhileStatement | ForStatement | IfStatement,
769                    CompoundStatement
770                ) == 0
771                {
772                    stats.lloc.logical_lines += 1;
773                }
774            }
775            _ => {
776                check_comment_ends_on_code_line(stats, start);
777                stats.ploc.lines.insert(start);
778            }
779        }
780    }
781}
782
783impl Loc for JavaCode {
784    fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
785        use Java::*;
786
787        let (start, end) = init(node, stats, is_func_space, is_unit);
788        let kind_id: Java = node.object().kind_id().into();
789        // LLOC in Java is counted for statements only
790        // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
791        match kind_id {
792            Program => {}
793            LineComment | BlockComment => {
794                add_cloc_lines(stats, start, end);
795            }
796            AssertStatement | BreakStatement | ContinueStatement | DoStatement
797            | EnhancedForStatement | ExpressionStatement | ForStatement | IfStatement
798            | ReturnStatement | SwitchExpression | ThrowStatement | TryStatement
799            | WhileStatement => {
800                stats.lloc.logical_lines += 1;
801            }
802            LocalVariableDeclaration => {
803                if count_specific_ancestors!(node, ForStatement, Block) == 0 {
804                    // The initializer, condition, and increment in a for loop are expressions.
805                    // Don't count the variable declaration if in a ForStatement.
806                    // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
807                    stats.lloc.logical_lines += 1;
808                }
809            }
810            _ => {
811                check_comment_ends_on_code_line(stats, start);
812                stats.ploc.lines.insert(start);
813            }
814        }
815    }
816}
817
818impl Loc for PreprocCode {}
819impl Loc for CcommentCode {}
820
821#[cfg(test)]
822mod tests {
823    use std::path::PathBuf;
824
825    use super::*;
826
827    #[test]
828    fn python_sloc() {
829        check_metrics!(
830            "
831
832            a = 42
833
834            ",
835            "foo.py",
836            PythonParser,
837            loc,
838            [(sloc, 1, usize), (sloc_min, 1, usize), (sloc_max, 1, usize)],
839            [(sloc_average, 1.0)] // The number of spaces is 1
840        );
841    }
842
843    #[test]
844    fn python_blank() {
845        check_metrics!(
846            "
847            a = 42
848
849            b = 43
850
851            ",
852            "foo.py",
853            PythonParser,
854            loc,
855            [
856                (blank, 1, usize),
857                (blank_min, 1, usize),
858                (blank_max, 1, usize)
859            ],
860            [(blank_average, 1.0)] // The number of spaces is 1
861        );
862    }
863
864    #[test]
865    fn rust_blank() {
866        check_metrics!(
867            "
868
869            let a = 42;
870
871            let b = 43;
872
873            ",
874            "foo.rs",
875            RustParser,
876            loc,
877            [
878                (blank, 1, usize),
879                (blank_min, 1, usize),
880                (blank_max, 1, usize)
881            ],
882            [(blank_average, 1.0)] // The number of spaces is 1
883        );
884
885        check_metrics!(
886            "fn func() { /* comment */ }",
887            "foo.rs",
888            RustParser,
889            loc,
890            [
891                (blank, 0, usize),
892                (blank_min, 0, usize),
893                (blank_max, 0, usize)
894            ],
895            [(blank_average, 0.0)] // The number of spaces is 2
896        );
897    }
898
899    #[test]
900    fn c_blank() {
901        check_metrics!(
902            "
903
904            int a = 42;
905
906            int b = 43;
907
908            ",
909            "foo.c",
910            CppParser,
911            loc,
912            [
913                (blank, 1, usize),
914                (blank_min, 1, usize),
915                (blank_max, 1, usize)
916            ],
917            [(blank_average, 1.0)] // The number of spaces is 1
918        );
919    }
920
921    #[test]
922    fn python_no_zero_blank() {
923        // Checks that the blank metric is not equal to 0 when there are some
924        // comments next to code lines.
925        check_metrics!(
926            "def ConnectToUpdateServer():
927                 pool = 4
928
929                 updateServer = -42
930                 isConnected = False
931                 currTry = 0
932                 numRetries = 10 # Number of IPC connection retries before
933                                 # giving up.
934                 numTries = 20 # Number of IPC connection tries before
935                               # giving up.",
936            "foo.py",
937            PythonParser,
938            loc,
939            [
940                (sloc, 10, usize), // The number of lines is 10
941                (ploc, 7, usize),  // The number of code lines is 7
942                (lloc, 6, usize),  // The number of statements is 6
943                (cloc, 4, usize),  // The number of comments is 4
944                (blank, 1, usize)  // The number of blank lines is 1
945                (sloc_min, 9, usize),
946                (ploc_min, 7, usize),
947                (lloc_min, 6, usize),
948                (cloc_min, 2, usize),
949                (blank_min, 1, usize),
950                (sloc_max, 9, usize),
951                (ploc_max, 7, usize),
952                (lloc_max, 6, usize),
953                (cloc_max, 2, usize),
954                (blank_max, 1, usize)
955            ],
956            [
957                (sloc_average, 5.0), // The number of spaces is 2
958                (ploc_average, 3.5),
959                (lloc_average, 3.0),
960                (cloc_average, 2.0),
961                (blank_average, 0.5)
962            ]
963        );
964    }
965
966    #[test]
967    fn python_no_blank() {
968        // Checks that the blank metric is equal to 0 when there are no blank
969        // lines and there are comments next to code lines.
970        check_metrics!(
971            "def ConnectToUpdateServer():
972                 pool = 4
973                 updateServer = -42
974                 isConnected = False
975                 currTry = 0
976                 numRetries = 10 # Number of IPC connection retries before
977                                 # giving up.
978                 numTries = 20 # Number of IPC connection tries before
979                               # giving up.",
980            "foo.py",
981            PythonParser,
982            loc,
983            [
984                (sloc, 9, usize),  // The number of lines is 9
985                (ploc, 7, usize),  // The number of code lines is 7
986                (lloc, 6, usize),  // The number of statements is 6
987                (cloc, 4, usize),  // The number of comments is 4
988                (blank, 0, usize), // The number of blank lines is 0
989                (sloc_min, 8, usize),
990                (ploc_min, 7, usize),
991                (lloc_min, 6, usize),
992                (cloc_min, 2, usize),
993                (blank_min, 0, usize),
994                (sloc_max, 8, usize),
995                (ploc_max, 7, usize),
996                (lloc_max, 6, usize),
997                (cloc_max, 2, usize),
998                (blank_max, 0, usize)
999            ],
1000            [
1001                (sloc_average, 4.5), // The number of spaces is 2
1002                (ploc_average, 3.5),
1003                (lloc_average, 3.0),
1004                (cloc_average, 2.0),
1005                (blank_average, 0.0)
1006            ]
1007        );
1008    }
1009
1010    #[test]
1011    fn python_no_zero_blank_more_comments() {
1012        // Checks that the blank metric is not equal to 0 when there are more
1013        // comments next to code lines compared to the previous tests.
1014        check_metrics!(
1015            "def ConnectToUpdateServer():
1016                 pool = 4
1017
1018                 updateServer = -42
1019                 isConnected = False
1020                 currTry = 0 # Set this variable to 0
1021                 numRetries = 10 # Number of IPC connection retries before
1022                                 # giving up.
1023                 numTries = 20 # Number of IPC connection tries before
1024                               # giving up.",
1025            "foo.py",
1026            PythonParser,
1027            loc,
1028            [
1029                (sloc, 10, usize), // The number of lines is 10
1030                (ploc, 7, usize),  // The number of code lines is 7
1031                (lloc, 6, usize),  // The number of statements is 6
1032                (cloc, 5, usize),  // The number of comments is 5
1033                (blank, 1, usize), // The number of blank lines is 1
1034                (sloc_min, 9, usize),
1035                (ploc_min, 7, usize),
1036                (lloc_min, 6, usize),
1037                (cloc_min, 3, usize),
1038                (blank_min, 1, usize),
1039                (sloc_max, 9, usize),
1040                (ploc_max, 7, usize),
1041                (lloc_max, 6, usize),
1042                (cloc_max, 3, usize),
1043                (blank_max, 1, usize)
1044            ],
1045            [
1046                (sloc_average, 5.0), // The number of spaces is 2
1047                (ploc_average, 3.5),
1048                (lloc_average, 3.0),
1049                (cloc_average, 2.5),
1050                (blank_average, 0.5)
1051            ]
1052        );
1053    }
1054
1055    #[test]
1056    fn rust_no_zero_blank() {
1057        // Checks that the blank metric is not equal to 0 when there are some
1058        // comments next to code lines.
1059        check_metrics!(
1060            "fn ConnectToUpdateServer() {
1061              let pool = 0;
1062
1063              let updateServer = -42;
1064              let isConnected = false;
1065              let currTry = 0;
1066              let numRetries = 10;  // Number of IPC connection retries before
1067                                    // giving up.
1068              let numTries = 20;    // Number of IPC connection tries before
1069                                    // giving up.
1070            }",
1071            "foo.rs",
1072            RustParser,
1073            loc,
1074            [
1075                (sloc, 11, usize), // The number of lines is 11
1076                (ploc, 8, usize),  // The number of code lines is 8
1077                (lloc, 6, usize),  // The number of statements is 6
1078                (cloc, 4, usize),  // The number of comments is 4
1079                (blank, 1, usize), // The number of blank lines is 1
1080                (sloc_min, 11, usize),
1081                (ploc_min, 8, usize),
1082                (lloc_min, 6, usize),
1083                (cloc_min, 4, usize),
1084                (blank_min, 1, usize),
1085                (sloc_max, 11, usize),
1086                (ploc_max, 8, usize),
1087                (lloc_max, 6, usize),
1088                (cloc_max, 4, usize),
1089                (blank_max, 1, usize)
1090            ],
1091            [
1092                (sloc_average, 5.5), // The number of spaces is 2
1093                (ploc_average, 4.0),
1094                (lloc_average, 3.0),
1095                (cloc_average, 2.0),
1096                (blank_average, 0.5)
1097            ]
1098        );
1099    }
1100
1101    #[test]
1102    fn javascript_no_zero_blank() {
1103        // Checks that the blank metric is not equal to 0 when there are some
1104        // comments next to code lines.
1105        check_metrics!(
1106            "function ConnectToUpdateServer() {
1107              var pool = 0;
1108
1109              var updateServer = -42;
1110              var isConnected = false;
1111              var currTry = 0;
1112              var numRetries = 10;  // Number of IPC connection retries before
1113                                    // giving up.
1114              var numTries = 20;    // Number of IPC connection tries before
1115                                    // giving up.
1116            }",
1117            "foo.js",
1118            JavascriptParser,
1119            loc,
1120            [
1121                (sloc, 11, usize), // The number of lines is 11
1122                (ploc, 8, usize),  // The number of code lines is 8
1123                (lloc, 1, usize),  // The number of statements is 1
1124                (cloc, 4, usize),  // The number of comments is 4
1125                (blank, 1, usize), // The number of blank lines is 1
1126                (sloc_min, 11, usize),
1127                (ploc_min, 8, usize),
1128                (lloc_min, 1, usize),
1129                (cloc_min, 4, usize),
1130                (blank_min, 1, usize),
1131                (sloc_max, 11, usize),
1132                (ploc_max, 8, usize),
1133                (lloc_max, 1, usize),
1134                (cloc_max, 4, usize),
1135                (blank_max, 1, usize)
1136            ],
1137            [
1138                (sloc_average, 5.5), // The number of spaces is 2
1139                (ploc_average, 4.0),
1140                (lloc_average, 0.5),
1141                (cloc_average, 2.0),
1142                (blank_average, 0.5),
1143            ]
1144        );
1145    }
1146
1147    #[test]
1148    fn cpp_no_zero_blank() {
1149        // Checks that the blank metric is not equal to 0 when there are some
1150        // comments next to code lines.
1151        check_metrics!(
1152            "void ConnectToUpdateServer() {
1153              int pool;
1154
1155              int updateServer = -42;
1156              bool isConnected = false;
1157              int currTry = 0;
1158              const int numRetries = 10; // Number of IPC connection retries before
1159                                         // giving up.
1160              const int numTries = 20; // Number of IPC connection tries before
1161                                       // giving up.
1162            }",
1163            "foo.cpp",
1164            CppParser,
1165            loc,
1166            [
1167                (sloc, 11, usize), // The number of lines is 11
1168                (ploc, 8, usize),  // The number of code lines is 8
1169                (lloc, 6, usize),  // The number of statements is 6
1170                (cloc, 4, usize),  // The number of comments is 4
1171                (blank, 1, usize), // The number of blank lines is 1
1172                (sloc_min, 11, usize),
1173                (ploc_min, 8, usize),
1174                (lloc_min, 6, usize),
1175                (cloc_min, 4, usize),
1176                (blank_min, 1, usize),
1177                (sloc_max, 11, usize),
1178                (ploc_max, 8, usize),
1179                (lloc_max, 6, usize),
1180                (cloc_max, 4, usize),
1181                (blank_max, 1, usize)
1182            ],
1183            [
1184                (sloc_average, 5.5), // The number of spaces is 2
1185                (ploc_average, 4.0),
1186                (lloc_average, 3.0),
1187                (cloc_average, 2.0),
1188                (blank_average, 0.5)
1189            ]
1190        );
1191    }
1192
1193    #[test]
1194    fn cpp_code_line_start_block_blank() {
1195        // Checks that the blank metric is equal to 1 when there are
1196        // block comments starting next to code lines.
1197        check_metrics!(
1198            "void ConnectToUpdateServer() {
1199              int pool;
1200
1201              int updateServer = -42;
1202              bool isConnected = false;
1203              int currTry = 0;
1204              const int numRetries = 10; /* Number of IPC connection retries
1205              before
1206              giving up. */
1207              const int numTries = 20; // Number of IPC connection tries before
1208                                       // giving up.
1209            }",
1210            "foo.cpp",
1211            CppParser,
1212            loc,
1213            [
1214                (sloc, 12, usize), // The number of lines is 12
1215                (ploc, 8, usize),  // The number of code lines is 8
1216                (lloc, 6, usize),  // The number of statements is 6
1217                (cloc, 5, usize),  // The number of comments is 5
1218                (blank, 1, usize), // The number of blank lines is 1
1219                (sloc_min, 12, usize),
1220                (ploc_min, 8, usize),
1221                (lloc_min, 6, usize),
1222                (cloc_min, 5, usize),
1223                (blank_min, 1, usize),
1224                (sloc_max, 12, usize),
1225                (ploc_max, 8, usize),
1226                (lloc_max, 6, usize),
1227                (cloc_max, 5, usize),
1228                (blank_max, 1, usize)
1229            ],
1230            [
1231                (sloc_average, 6.0), // The number of spaces is 2
1232                (ploc_average, 4.0),
1233                (lloc_average, 3.0),
1234                (cloc_average, 2.5),
1235                (blank_average, 0.5)
1236            ]
1237        );
1238    }
1239
1240    #[test]
1241    fn cpp_block_comment_blank() {
1242        // Checks that the blank metric is equal to 1 when there are
1243        // block comments on independent lines.
1244        check_metrics!(
1245            "void ConnectToUpdateServer() {
1246              int pool;
1247
1248              int updateServer = -42;
1249              bool isConnected = false;
1250              int currTry = 0;
1251              /* Number of IPC connection retries
1252              before
1253              giving up. */
1254              const int numRetries = 10;
1255              const int numTries = 20; // Number of IPC connection tries before
1256                                       // giving up.
1257            }",
1258            "foo.cpp",
1259            CppParser,
1260            loc,
1261            [
1262                (sloc, 13, usize), // The number of lines is 13
1263                (ploc, 8, usize),  // The number of code lines is 8
1264                (lloc, 6, usize),  // The number of statements is 6
1265                (cloc, 5, usize),  // The number of comments is 5
1266                (blank, 1, usize), // The number of blank lines is 1
1267                (sloc_min, 13, usize),
1268                (ploc_min, 8, usize),
1269                (lloc_min, 6, usize),
1270                (cloc_min, 5, usize),
1271                (blank_min, 1, usize),
1272                (sloc_max, 13, usize),
1273                (ploc_max, 8, usize),
1274                (lloc_max, 6, usize),
1275                (cloc_max, 5, usize),
1276                (blank_max, 1, usize)
1277            ],
1278            [
1279                (sloc_average, 6.5), // The number of spaces is 2
1280                (ploc_average, 4.0),
1281                (lloc_average, 3.0),
1282                (cloc_average, 2.5),
1283                (blank_average, 0.5)
1284            ]
1285        );
1286    }
1287
1288    #[test]
1289    fn cpp_code_line_block_one_line_blank() {
1290        // Checks that the blank metric is equal to 1 when there are
1291        // block comments before the same code line.
1292        check_metrics!(
1293            "void ConnectToUpdateServer() {
1294              int pool;
1295
1296              int updateServer = -42;
1297              bool isConnected = false;
1298              int currTry = 0;
1299              /* Number of IPC connection retries before giving up. */ const int numRetries = 10;
1300              const int numTries = 20; // Number of IPC connection tries before
1301                                       // giving up.
1302            }",
1303            "foo.cpp",
1304            CppParser,
1305            loc,
1306            [
1307                (sloc, 10, usize), // The number of lines is 10
1308                (ploc, 8, usize),  // The number of code lines is 8
1309                (lloc, 6, usize),  // The number of statements is 6
1310                (cloc, 3, usize),  // The number of comments is 3
1311                (blank, 1, usize), // The number of blank lines is 1
1312                (sloc_min, 10, usize),
1313                (ploc_min, 8, usize),
1314                (lloc_min, 6, usize),
1315                (cloc_min, 3, usize),
1316                (blank_min, 1, usize),
1317                (sloc_max, 10, usize),
1318                (ploc_max, 8, usize),
1319                (lloc_max, 6, usize),
1320                (cloc_max, 3, usize),
1321                (blank_max, 1, usize)
1322            ],
1323            [
1324                (sloc_average, 5.0), // The number of spaces is 2
1325                (ploc_average, 4.0),
1326                (lloc_average, 3.0),
1327                (cloc_average, 1.5),
1328                (blank_average, 0.5)
1329            ]
1330        );
1331    }
1332
1333    #[test]
1334    fn cpp_code_line_end_block_blank() {
1335        // Checks that the blank metric is equal to 1 when there are
1336        // block comments ending next to code lines.
1337        check_metrics!(
1338            "void ConnectToUpdateServer() {
1339              int pool;
1340
1341              int updateServer = -42;
1342              bool isConnected = false;
1343              int currTry = 0;
1344              /* Number of IPC connection retries
1345              before
1346              giving up. */ const int numRetries = 10;
1347              const int numTries = 20; // Number of IPC connection tries before
1348                                       // giving up.
1349            }",
1350            "foo.cpp",
1351            CppParser,
1352            loc,
1353            [
1354                (sloc, 12, usize), // The number of lines is 12
1355                (ploc, 8, usize),  // The number of code lines is 8
1356                (lloc, 6, usize),  // The number of statements is 6
1357                (cloc, 5, usize),  // The number of comments is 5
1358                (blank, 1, usize), // The number of blank lines is 1
1359                (sloc_min, 12, usize),
1360                (ploc_min, 8, usize),
1361                (lloc_min, 6, usize),
1362                (cloc_min, 5, usize),
1363                (blank_min, 1, usize),
1364                (sloc_max, 12, usize),
1365                (ploc_max, 8, usize),
1366                (lloc_max, 6, usize),
1367                (cloc_max, 5, usize),
1368                (blank_max, 1, usize)
1369            ],
1370            [
1371                (sloc_average, 6.0), // The number of spaces is 2
1372                (ploc_average, 4.0),
1373                (lloc_average, 3.0),
1374                (cloc_average, 2.5),
1375                (blank_average, 0.5)
1376            ]
1377        );
1378    }
1379
1380    #[test]
1381    fn python_cloc() {
1382        check_metrics!(
1383            "\"\"\"Block comment
1384            Block comment
1385            \"\"\"
1386            # Line Comment
1387            a = 42 # Line Comment",
1388            "foo.py",
1389            PythonParser,
1390            loc,
1391            [(cloc, 5, usize), (cloc_min, 5, usize), (cloc_max, 5, usize)],
1392            [(cloc_average, 5.0)] // The number of spaces is 1
1393        );
1394    }
1395
1396    #[test]
1397    fn rust_cloc() {
1398        check_metrics!(
1399            "/*Block comment
1400            Block Comment*/
1401            //Line Comment
1402            /*Block Comment*/ let a = 42; // Line Comment",
1403            "foo.rs",
1404            RustParser,
1405            loc,
1406            [(cloc, 5, usize), (cloc_min, 5, usize), (cloc_max, 5, usize)],
1407            [(cloc_average, 5.0)] // The number of spaces is 1
1408        );
1409    }
1410
1411    #[test]
1412    fn c_cloc() {
1413        check_metrics!(
1414            "/*Block comment
1415            Block Comment*/
1416            //Line Comment
1417            /*Block Comment*/ int a = 42; // Line Comment",
1418            "foo.c",
1419            CppParser,
1420            loc,
1421            [(cloc, 5, usize), (cloc_min, 5, usize), (cloc_max, 5, usize)],
1422            [(cloc_average, 5.0)] // The number of spaces is 1
1423        );
1424    }
1425
1426    #[test]
1427    fn python_lloc() {
1428        check_metrics!(
1429            "for x in range(0,42):
1430                if x % 2 == 0:
1431                    print(x)",
1432            "foo.py",
1433            PythonParser,
1434            loc,
1435            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1436            [(lloc_average, 3.0)] // The number of spaces is 1
1437        );
1438    }
1439
1440    #[test]
1441    fn rust_lloc() {
1442        check_metrics!(
1443            "for x in 0..42 {
1444                if x % 2 == 0 {
1445                    println!(\"{}\", x);
1446                }
1447             }",
1448            "foo.rs",
1449            RustParser,
1450            loc,
1451            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1452            [(lloc_average, 3.0)] // The number of spaces is 1
1453        );
1454
1455        // LLOC returns three because there is an empty Rust statement
1456        check_metrics!(
1457            "let a = 42;
1458             if true {
1459                42
1460             } else {
1461                43
1462             };",
1463            "foo.rs",
1464            RustParser,
1465            loc,
1466            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1467            [(lloc_average, 3.0)] // The number of spaces is 1
1468        );
1469    }
1470
1471    #[test]
1472    fn c_lloc() {
1473        check_metrics!(
1474            "for (;;)
1475                break;",
1476            "foo.c",
1477            CppParser,
1478            loc,
1479            [(lloc, 2, usize), (lloc_min, 2, usize), (lloc_max, 2, usize)],
1480            [(lloc_average, 2.0)] // The number of spaces is 1
1481        );
1482    }
1483
1484    #[test]
1485    fn cpp_lloc() {
1486        check_metrics!(
1487            "nsTArray<xpcGCCallback> callbacks(extraGCCallbacks.Clone());
1488             for (uint32_t i = 0; i < callbacks.Length(); ++i) {
1489                 callbacks[i](status);
1490             }",
1491            "foo.cpp",
1492            CppParser,
1493            loc,
1494            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)], // nsTArray, for, callbacks
1495            [(lloc_average, 3.0)] // The number of spaces is 1
1496        );
1497    }
1498
1499    #[test]
1500    fn cpp_return_lloc() {
1501        check_metrics!(
1502            "uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
1503             return RgbaColor(pixel_data) == blank_pixel_;",
1504            "foo.cpp",
1505            CppParser,
1506            loc,
1507            [(lloc, 2, usize), (lloc_min, 2, usize), (lloc_max, 2, usize)], // pixel_data, return
1508            [(lloc_average, 2.0)] // The number of spaces is 1
1509        );
1510    }
1511
1512    #[test]
1513    fn cpp_for_lloc() {
1514        check_metrics!(
1515            "for (; start != end; ++start) {
1516                 const unsigned char idx = *start;
1517                 if (idx > 127 || !kValidTokenMap[idx]) return false;
1518             }",
1519            "foo.cpp",
1520            CppParser,
1521            loc,
1522            [(lloc, 4, usize), (lloc_min, 4, usize), (lloc_max, 4, usize)], // for, idx, if, return
1523            [(lloc_average, 4.0)] // The number of spaces is 1
1524        );
1525    }
1526
1527    #[test]
1528    fn cpp_while_lloc() {
1529        check_metrics!(
1530            "while (sHeapAtoms) {
1531                 HttpHeapAtom* next = sHeapAtoms->next;
1532                 free(sHeapAtoms);
1533            }",
1534            "foo.cpp",
1535            CppParser,
1536            loc,
1537            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)], // while, next, free,
1538            [(lloc_average, 3.0)] // The number of spaces is 1
1539        );
1540    }
1541
1542    #[test]
1543    fn python_string_on_new_line() {
1544        // More lines of the same instruction were counted as blank lines
1545        check_metrics!(
1546            "capabilities[\"goog:chromeOptions\"][\"androidPackage\"] = \\
1547                \"org.chromium.weblayer.shell\"",
1548            "foo.py",
1549            PythonParser,
1550            loc,
1551            [
1552                (sloc, 2, usize),
1553                (ploc, 2, usize),
1554                (lloc, 1, usize),
1555                (cloc, 0, usize),
1556                (blank, 0, usize),
1557                (sloc_min, 2, usize),
1558                (ploc_min, 2, usize),
1559                (lloc_min, 1, usize),
1560                (cloc_min, 0, usize),
1561                (blank_min, 0, usize),
1562                (sloc_max, 2, usize),
1563                (ploc_max, 2, usize),
1564                (lloc_max, 1, usize),
1565                (cloc_max, 0, usize),
1566                (blank_max, 0, usize)
1567            ],
1568            [
1569                (sloc_average, 2.0), // The number of spaces is 1
1570                (ploc_average, 2.0),
1571                (lloc_average, 1.0),
1572                (cloc_average, 0.0),
1573                (blank_average, 0.0)
1574            ]
1575        );
1576    }
1577
1578    #[test]
1579    fn rust_no_field_expression_lloc() {
1580        check_metrics!(
1581            "struct Foo {
1582                field: usize,
1583             }
1584             let foo = Foo { 42 };
1585             foo.field;",
1586            "foo.rs",
1587            RustParser,
1588            loc,
1589            [(lloc, 2, usize), (lloc_min, 2, usize), (lloc_max, 2, usize)],
1590            [(lloc_average, 2.0)] // The number of spaces is 1
1591        );
1592    }
1593
1594    #[test]
1595    fn rust_no_parenthesized_expression_lloc() {
1596        check_metrics!(
1597            "let a = (42 + 0);",
1598            "foo.rs",
1599            RustParser,
1600            loc,
1601            [(lloc, 1, usize), (lloc_min, 1, usize), (lloc_max, 1, usize)],
1602            [(lloc_average, 1.0)] // The number of spaces is 1
1603        );
1604    }
1605
1606    #[test]
1607    fn rust_no_array_expression_lloc() {
1608        check_metrics!(
1609            "let a = [0; 42];",
1610            "foo.rs",
1611            RustParser,
1612            loc,
1613            [(lloc, 1, usize), (lloc_min, 1, usize), (lloc_max, 1, usize)],
1614            [(lloc_average, 1.0)] // The number of spaces is 1
1615        );
1616    }
1617
1618    #[test]
1619    fn rust_no_tuple_expression_lloc() {
1620        check_metrics!(
1621            "let a = (0, 42);",
1622            "foo.rs",
1623            RustParser,
1624            loc,
1625            [(lloc, 1, usize), (lloc_min, 1, usize), (lloc_max, 1, usize)],
1626            [(lloc_average, 1.0)] // The number of spaces is 1
1627        );
1628    }
1629
1630    #[test]
1631    fn rust_no_unit_expression_lloc() {
1632        check_metrics!(
1633            "let a = ();",
1634            "foo.rs",
1635            RustParser,
1636            loc,
1637            [(lloc, 1, usize), (lloc_min, 1, usize), (lloc_max, 1, usize)],
1638            [(lloc_average, 1.0)] // The number of spaces is 1
1639        );
1640    }
1641
1642    #[test]
1643    fn rust_call_function_lloc() {
1644        check_metrics!(
1645            "let a = foo(); // +1
1646             foo(); // +1
1647             k!(foo()); // +1",
1648            "foo.rs",
1649            RustParser,
1650            loc,
1651            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1652            [(lloc_average, 3.0)] // The number of spaces is 1
1653        );
1654    }
1655
1656    #[test]
1657    fn rust_macro_invocation_lloc() {
1658        check_metrics!(
1659            "let a = foo!(); // +1
1660             foo!(); // +1
1661             k(foo!()); // +1",
1662            "foo.rs",
1663            RustParser,
1664            loc,
1665            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1666            [(lloc_average, 3.0)] // The number of spaces is 1
1667        );
1668    }
1669
1670    #[test]
1671    fn rust_function_in_loop_lloc() {
1672        check_metrics!(
1673            "for (a, b) in c.iter().enumerate() {} // +1
1674             while (a, b) in c.iter().enumerate() {} // +1
1675             while let Some(a) = c.strip_prefix(\"hi\") {} // +1",
1676            "foo.rs",
1677            RustParser,
1678            loc,
1679            [(lloc, 3, usize), (lloc_min, 3, usize), (lloc_max, 3, usize)],
1680            [(lloc_average, 3.0)] // The number of spaces is 1
1681        );
1682    }
1683
1684    #[test]
1685    fn rust_function_in_if_lloc() {
1686        check_metrics!(
1687            "if foo() {} // +1
1688             if let Some(a) = foo() {} // +1",
1689            "foo.rs",
1690            RustParser,
1691            loc,
1692            [(lloc, 2, usize), (lloc_min, 2, usize), (lloc_max, 2, usize)],
1693            [(lloc_average, 2.0)] // The number of spaces is 1
1694        );
1695    }
1696
1697    #[test]
1698    fn rust_function_in_return_lloc() {
1699        check_metrics!(
1700            "return foo();
1701             await foo();",
1702            "foo.rs",
1703            RustParser,
1704            loc,
1705            [(lloc, 2, usize), (lloc_min, 2, usize), (lloc_max, 2, usize)],
1706            [(lloc_average, 2.0)] // The number of spaces is 1
1707        );
1708    }
1709
1710    #[test]
1711    fn rust_closure_expression_lloc() {
1712        check_metrics!(
1713            "let a = |i: i32| -> i32 { i + 1 }; // +1
1714             a(42); // +1
1715             k(b.iter().map(|n| n.parse.ok().unwrap_or(42))); // +1",
1716            "foo.rs",
1717            RustParser,
1718            loc,
1719            [(lloc, 3, usize), (lloc_min, 0, usize), (lloc_max, 0, usize)],
1720            [(lloc_average, 1.0)] // The number of spaces is 3
1721        );
1722    }
1723
1724    #[test]
1725    fn python_general_loc() {
1726        check_metrics!(
1727            "def func(a,
1728                      b,
1729                      c):
1730                 print(a)
1731                 print(b)
1732                 print(c)",
1733            "foo.py",
1734            PythonParser,
1735            loc,
1736            [
1737                (sloc, 6, usize),  // The number of lines is 6
1738                (ploc, 6, usize),  // The number of code lines is 6
1739                (lloc, 3, usize),  // The number of statements is 3 (print)
1740                (cloc, 0, usize),  // The number of comments is 0
1741                (blank, 0, usize), // The number of blank lines is 0
1742                (sloc_min, 6, usize),
1743                (ploc_min, 6, usize),
1744                (lloc_min, 3, usize),
1745                (cloc_min, 0, usize),
1746                (blank_min, 0, usize),
1747                (sloc_max, 6, usize),
1748                (ploc_max, 6, usize),
1749                (lloc_max, 3, usize),
1750                (cloc_max, 0, usize),
1751                (blank_max, 0, usize)
1752            ],
1753            [
1754                (sloc_average, 3.0), // The number of spaces is 2
1755                (ploc_average, 3.0),
1756                (lloc_average, 1.5),
1757                (cloc_average, 0.0),
1758                (blank_average, 0.0)
1759            ]
1760        );
1761    }
1762
1763    #[test]
1764    fn python_real_loc() {
1765        check_metrics!(
1766            "def web_socket_transfer_data(request):
1767                while True:
1768                    line = request.ws_stream.receive_message()
1769                    if line is None:
1770                        return
1771                    code, reason = line.split(' ', 1)
1772                    if code is None or reason is None:
1773                        return
1774                    request.ws_stream.close_connection(int(code), reason)
1775                    # close_connection() initiates closing handshake. It validates code
1776                    # and reason. If you want to send a broken close frame for a test,
1777                    # following code will be useful.
1778                    # > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
1779                    # > request.connection.write(stream.create_close_frame(data))
1780                    # > # Suppress to re-respond client responding close frame.
1781                    # > raise Exception(\"customized server initiated closing handshake\")",
1782            "foo.py",
1783            PythonParser,
1784            loc,
1785            [
1786                (sloc, 16, usize), // The number of lines is 16
1787                (ploc, 9, usize),  // The number of code lines is 9
1788                (lloc, 8, usize),  // The number of statements is 8
1789                (cloc, 7, usize),  // The number of comments is 7
1790                (blank, 0, usize), // The number of blank lines is 0
1791                (sloc_min, 16, usize),
1792                (ploc_min, 9, usize),
1793                (lloc_min, 8, usize),
1794                (cloc_min, 7, usize),
1795                (blank_min, 0, usize),
1796                (sloc_max, 16, usize),
1797                (ploc_max, 9, usize),
1798                (lloc_max, 8, usize),
1799                (cloc_max, 7, usize),
1800                (blank_max, 0, usize)
1801            ],
1802            [
1803                (sloc_average, 8.0), // The number of spaces is 2
1804                (ploc_average, 4.5),
1805                (lloc_average, 4.0),
1806                (cloc_average, 3.5),
1807                (blank_average, 0.0)
1808            ]
1809        );
1810    }
1811
1812    #[test]
1813    fn javascript_real_loc() {
1814        check_metrics!(
1815            "assert.throws(Test262Error, function() {
1816               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
1817                 return;
1818               }
1819             });",
1820            "foo.js",
1821            JavascriptParser,
1822            loc,
1823            [
1824                (sloc, 5, usize),  // The number of lines is 5
1825                (ploc, 5, usize),  // The number of code lines is 5
1826                (lloc, 6, usize),  // The number of statements is 6
1827                (cloc, 0, usize),  // The number of comments is 0
1828                (blank, 0, usize), // The number of blank lines is 0
1829                (sloc_min, 5, usize),
1830                (ploc_min, 5, usize),
1831                (lloc_min, 5, usize),
1832                (cloc_min, 0, usize),
1833                (blank_min, 0, usize),
1834                (sloc_max, 5, usize),
1835                (ploc_max, 5, usize),
1836                (lloc_max, 5, usize),
1837                (cloc_max, 0, usize),
1838                (blank_max, 0, usize)
1839            ],
1840            [
1841                (sloc_average, 2.5), // The number of spaces is 2
1842                (ploc_average, 2.5),
1843                (lloc_average, 3.0),
1844                (cloc_average, 0.0),
1845                (blank_average, 0.0)
1846            ]
1847        );
1848    }
1849
1850    #[test]
1851    fn mozjs_real_loc() {
1852        check_metrics!(
1853            "assert.throws(Test262Error, function() {
1854               for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
1855                 return;
1856               }
1857             });",
1858            "foo.js",
1859            MozjsParser,
1860            loc,
1861            [
1862                (sloc, 5, usize),  // The number of lines is 5
1863                (ploc, 5, usize),  // The number of code lines is 5
1864                (lloc, 6, usize),  // The number of statements is 6
1865                (cloc, 0, usize),  // The number of comments is 0
1866                (blank, 0, usize), // The number of blank lines is 0
1867                (sloc_min, 5, usize),
1868                (ploc_min, 5, usize),
1869                (lloc_min, 5, usize),
1870                (cloc_min, 0, usize),
1871                (blank_min, 0, usize),
1872                (sloc_max, 5, usize),
1873                (ploc_max, 5, usize),
1874                (lloc_max, 5, usize),
1875                (cloc_max, 0, usize),
1876                (blank_max, 0, usize)
1877            ],
1878            [
1879                (sloc_average, 2.5), // The number of spaces is 2
1880                (ploc_average, 2.5),
1881                (lloc_average, 3.0),
1882                (cloc_average, 0.0),
1883                (blank_average, 0.0)
1884            ]
1885        );
1886    }
1887
1888    #[test]
1889    fn cpp_namespace_loc() {
1890        check_metrics!(
1891            "namespace mozilla::dom::quota {} // namespace mozilla::dom::quota",
1892            "foo.cpp",
1893            CppParser,
1894            loc,
1895            [
1896                (sloc, 1, usize),  // The number of lines is 1
1897                (ploc, 1, usize),  // The number of code lines is 1
1898                (lloc, 0, usize),  // The number of statements is 0
1899                (cloc, 1, usize),  // The number of comments is 1
1900                (blank, 0, usize), // The number of blank lines is 0
1901                (sloc_min, 1, usize),
1902                (ploc_min, 1, usize),
1903                (lloc_min, 0, usize),
1904                (cloc_min, 0, usize),
1905                (blank_min, 0, usize),
1906                (sloc_max, 1, usize),
1907                (ploc_max, 1, usize),
1908                (lloc_max, 0, usize),
1909                (cloc_max, 0, usize),
1910                (blank_max, 0, usize)
1911            ],
1912            [
1913                (sloc_average, 0.5), // The number of spaces is 2
1914                (ploc_average, 0.5),
1915                (lloc_average, 0.0),
1916                (cloc_average, 0.5),
1917                (blank_average, 0.0)
1918            ]
1919        );
1920    }
1921
1922    #[test]
1923    fn java_comments() {
1924        check_metrics!(
1925            "for (int i = 0; i < 100; i++) { \
1926               // Print hello
1927               System.out.println(\"hello\"); \
1928               // Print world
1929               System.out.println(\"hello\"); \
1930             }",
1931            "foo.java",
1932            JavaParser,
1933            loc,
1934            [
1935                (cloc, 2, usize), // The number of comments is 2
1936            ]
1937        );
1938    }
1939
1940    #[test]
1941    fn java_blank() {
1942        check_metrics!(
1943            "int x = 1;
1944
1945
1946            int y = 2;",
1947            "foo.java",
1948            JavaParser,
1949            loc,
1950            [
1951                (blank, 2, usize), // The number of blank lines is 2
1952            ]
1953        );
1954    }
1955
1956    #[test]
1957    fn java_sloc() {
1958        check_metrics!(
1959            "for (int i = 0; i < 100; i++) {
1960               System.out.println(i);
1961             }",
1962            "foo.java",
1963            JavaParser,
1964            loc,
1965            [
1966                (sloc, 3, usize), // The number of lines is 3
1967            ]
1968        );
1969    }
1970
1971    #[test]
1972    fn java_module_sloc() {
1973        check_metrics!(
1974            "module helloworld{
1975              exports com.test;
1976            }",
1977            "foo.java",
1978            JavaParser,
1979            loc,
1980            [
1981                (sloc, 3, usize), // The number of lines is 3
1982            ]
1983        );
1984    }
1985
1986    #[test]
1987    fn java_single_ploc() {
1988        check_metrics!(
1989            "int x = 1;",
1990            "foo.java",
1991            JavaParser,
1992            loc,
1993            [
1994                (ploc, 1, usize), // The number of code lines is 1
1995            ]
1996        );
1997    }
1998
1999    #[test]
2000    fn java_simple_ploc() {
2001        check_metrics!(
2002            "for (int i = 0; i < 100; i = i++) {
2003               System.out.println(i);
2004             }",
2005            "foo.java",
2006            JavaParser,
2007            loc,
2008            [
2009                (ploc, 3, usize), // The number of code lines is 3
2010            ]
2011        );
2012    }
2013
2014    #[test]
2015    fn java_multi_ploc() {
2016        check_metrics!(
2017            "int x = 1;
2018            for (int i = 0; i < 100; i++) {
2019               System.out.println(i);
2020             }",
2021            "foo.java",
2022            JavaParser,
2023            loc,
2024            [
2025                (ploc, 4, usize), // The number of code lines is 4
2026            ]
2027        );
2028    }
2029
2030    #[test]
2031    fn java_single_statement_lloc() {
2032        check_metrics!(
2033            "int max = 10;",
2034            "foo.java",
2035            JavaParser,
2036            loc,
2037            [
2038                (lloc, 1, usize), // The number of statements is 1
2039            ]
2040        );
2041    }
2042
2043    #[test]
2044    fn java_for_lloc() {
2045        check_metrics!(
2046            "for (int i = 0; i < 100; i++) { // + 1
2047               System.out.println(i); // + 1
2048             }",
2049            "foo.java",
2050            JavaParser,
2051            loc,
2052            [
2053                (lloc, 2, usize), // The number of statements is 2
2054            ]
2055        );
2056    }
2057
2058    #[test]
2059    fn java_foreach_lloc() {
2060        check_metrics!(
2061            "
2062            int arr[]={12,13,14,44}; // +1
2063            for (int i:arr) { // +1
2064               System.out.println(i); // +1
2065             }",
2066            "foo.java",
2067            JavaParser,
2068            loc,
2069            [
2070                (lloc, 3, usize), // The number of statements is 3
2071            ]
2072        );
2073    }
2074
2075    #[test]
2076    fn java_while_lloc() {
2077        check_metrics!(
2078            "
2079            int i=0; // +1
2080            while(i < 10) { // +1
2081                i++; // +1
2082                System.out.println(i); // +1
2083             }",
2084            "foo.java",
2085            JavaParser,
2086            loc,
2087            [
2088                (lloc, 4, usize), // The number of statements is 4
2089            ]
2090        );
2091    }
2092
2093    #[test]
2094    fn java_do_while_lloc() {
2095        check_metrics!(
2096            "
2097            int i=0; // +1
2098            do { // +1
2099                i++; // +1
2100                System.out.println(i); // +1
2101             } while(i < 10)",
2102            "foo.java",
2103            JavaParser,
2104            loc,
2105            [
2106                (lloc, 4, usize), // The number of statements is 4
2107            ]
2108        );
2109    }
2110
2111    #[test]
2112    fn java_switch_lloc() {
2113        check_metrics!(
2114            "switch(grade) { // +1
2115                case 'A' :
2116                   System.out.println(\"Pass with distinction\"); // +1
2117                   break; // +1
2118                case 'B' :
2119                case 'C' :
2120                   System.out.println(\"Pass\"); // +1
2121                   break; // +1
2122                case 'D' :
2123                   System.out.println(\"At risk\"); // +1
2124                case 'F' :
2125                   System.out.println(\"Fail\"); // +1
2126                   break; // +1
2127                default :
2128                   System.out.println(\"Invalid grade\"); // +1
2129             }",
2130            "foo.java",
2131            JavaParser,
2132            loc,
2133            [
2134                (lloc, 9, usize), // The number of statements is 6
2135            ]
2136        );
2137    }
2138
2139    #[test]
2140    fn java_continue_lloc() {
2141        check_metrics!(
2142            "int max = 10; // +1
2143
2144            for (int i = 0; i < max; i++) { // +1
2145                if(i % 2 == 0) { continue;} + 2
2146                System.out.println(i); // +1
2147             }",
2148            "foo.java",
2149            JavaParser,
2150            loc,
2151            [
2152                (lloc, 5, usize), // The number of statements is 5
2153            ]
2154        );
2155    }
2156
2157    #[test]
2158    fn java_try_lloc() {
2159        check_metrics!(
2160            "try { // +1
2161                int[] myNumbers = {1, 2, 3}; // +1
2162                System.out.println(myNumbers[10]); // +1
2163              } catch (Exception e) {
2164                System.out.println(e.getMessage()); // +1
2165                throw e; // +1
2166              }",
2167            "foo.java",
2168            JavaParser,
2169            loc,
2170            [
2171                (lloc, 5, usize), // The number of statements is 5
2172            ]
2173        );
2174    }
2175
2176    #[test]
2177    fn java_class_loc() {
2178        check_metrics!(
2179            "
2180            public class Person {
2181              private String name;
2182              public Person(String name){
2183                this.name = name; // +1
2184              }
2185              public String getName() {
2186                return name; // +1
2187              }
2188            }",
2189            "foo.java",
2190            JavaParser,
2191            loc,
2192            [
2193                (lloc, 2, usize), // The number of statements is 2
2194            ]
2195        );
2196    }
2197
2198    #[test]
2199    fn java_expressions_lloc() {
2200        check_metrics!(
2201            "int x = 10;                                                            // +1 local var declaration
2202            x=+89;                                                                  // +1 expression statement
2203            int y = x * 2;                                                          // +1 local var declaration
2204            IntFunction double = (n) -> n*2;                                        // +1 local var declaration
2205            int y2 = double(x);                                                     // +1 local var declaration
2206            System.out.println(\"double \" + x + \" = \" + y2);                     // +1 expression statement
2207            String message = (x % 2) == 0 ? \"Evenly done.\" : \"Oddly done.\";     // +1 local var declaration
2208            Object done = (Runnable) () -> { System.out.println(\"Done!\"); };      // +2 local var declaration + expression statement
2209            String s = \"string\";                                                  // +1 local var declaration
2210            boolean isS = (s instanceof String);                                    // +1 local var declaration
2211            done.run();                                                             // +1 expression statement
2212            ",
2213            "foo.java",
2214            JavaParser,
2215            loc,
2216            [
2217                (lloc, 12, usize), // The number of statements is 12
2218            ]
2219        );
2220    }
2221
2222    #[test]
2223    fn java_statement_inline_loc() {
2224        check_metrics!(
2225            "for (int i = 0; i < 100; i++) { System.out.println(\"hello\"); }",
2226            "foo.java",
2227            JavaParser,
2228            loc,
2229            [
2230                (ploc, 1, usize), // The number of code lines is 1
2231                (lloc, 2, usize), // The number of statements is 2
2232                (cloc, 0, usize), // The number of comments is 0
2233            ]
2234        );
2235    }
2236
2237    #[test]
2238    fn java_general_loc() {
2239        check_metrics!(
2240            "int max = 100;
2241
2242            /*
2243              Loop through and print
2244                from: 0
2245                to: max
2246            */
2247            for (int i = 0; i < max; i++) {
2248               // Print the value
2249               System.out.println(i);
2250             }",
2251            "foo.java",
2252            JavaParser,
2253            loc,
2254            [
2255                (sloc, 11, usize), // The number of lines is 11
2256                (ploc, 4, usize),  // The number of code lines is 4
2257                (lloc, 3, usize),  // The number of statements is 3
2258                (cloc, 6, usize),  // The number of comments is 6
2259                (blank, 1, usize)  // The number of blank lines is 1
2260            ]
2261        );
2262    }
2263
2264    #[test]
2265    fn java_main_class_loc() {
2266        check_metrics!(
2267            "package com.company;
2268             /**
2269             * The HelloWorldApp class implements an application that
2270             * simply prints \"Hello World!\" to standard output.
2271             */
2272
2273            class HelloWorldApp {
2274              public void main(String[] args) {
2275                String message = args.length == 0 ? \"Hello empty world\" : \"Hello world\"; // +1 lloc : 1 var assignment
2276                System.out.println(message); // Display the string. +1 lloc
2277              }
2278            }",
2279            "foo.java",
2280            JavaParser,
2281            loc,
2282            [
2283                (sloc, 12, usize), // The number of lines is 12
2284                (ploc, 7, usize),  // The number of code lines is 7
2285                (lloc, 2, usize),  // The number of statements is 2
2286                (cloc, 6, usize),  // The number of comments is 6
2287                (blank, 1, usize)  // The number of blank lines is 1
2288            ]
2289        );
2290    }
2291}