From bcb3ca8f7a9ab0168fc2e3121dce4278b3a82676 Mon Sep 17 00:00:00 2001 From: Devon Burriss Date: Wed, 23 Mar 2022 10:11:08 +0100 Subject: [PATCH] Java LoC metric (#694) * WIP of Java LoC metric * Enables 2 passing simple tests * Adds more individual loc tests * Initial impl of JavaCode Getter trait get_space_kind was needed to fix an off by one issue with ploc * Test cleanup * Fix test * Fixes handling of for loops * Adds while * Remove println * Ran fmt * Reduce to handle only SLOC off by 1 * Adds ternary lloc to test * Adds expression grammar lloc * Removes expressions from count * Do not count expressions in for loop * Removes debugging lines * Fixes for the recent Stats changes * Defines space kinds * Adds interface space --- src/getter.rs | 14 +- src/metrics/loc.rs | 406 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 418 insertions(+), 2 deletions(-) diff --git a/src/getter.rs b/src/getter.rs index 3d3bcafc2..ec7f95828 100644 --- a/src/getter.rs +++ b/src/getter.rs @@ -509,4 +509,16 @@ impl Getter for CppCode { impl Getter for PreprocCode {} impl Getter for CcommentCode {} -impl Getter for JavaCode {} +impl Getter for JavaCode { + fn get_space_kind(node: &Node) -> SpaceKind { + use Java::*; + + let typ = node.object().kind_id(); + match typ.into() { + InterfaceDeclaration | ClassDeclaration => SpaceKind::Class, + MethodDeclaration | LambdaExpression => SpaceKind::Function, + Program => SpaceKind::Unit, + _ => SpaceKind::Unknown, + } + } +} diff --git a/src/metrics/loc.rs b/src/metrics/loc.rs index 7a9aaf2fd..9bf0ddb59 100644 --- a/src/metrics/loc.rs +++ b/src/metrics/loc.rs @@ -810,9 +810,43 @@ impl Loc for CppCode { } } +impl Loc for JavaCode { + fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) { + use Java::*; + + let (start, end) = init(node, stats, is_func_space, is_unit); + let kind_id: Java = node.object().kind_id().into(); + // LLOC in Java is counted for statements only + // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html + match kind_id { + Program => {} + Comment => { + add_cloc_lines(stats, start, end); + } + AssertStatement | BreakStatement | ContinueStatement | DoStatement + | EnhancedForStatement | ExpressionStatement | ForStatement | IfStatement + | ReturnStatement | SwitchStatement | ThrowStatement | TryStatement + | WhileStatement => { + stats.lloc.logical_lines += 1; + } + LocalVariableDeclaration => { + if count_specific_ancestors!(node, ForStatement, Block) == 0 { + // The initializer, condition, and increment in a for loop are expressions. + // Don't count the variable declaration if in a ForStatement. + // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html + stats.lloc.logical_lines += 1; + } + } + _ => { + check_comment_ends_on_code_line(stats, start); + stats.ploc.lines.insert(start); + } + } + } +} + impl Loc for PreprocCode {} impl Loc for CcommentCode {} -impl Loc for JavaCode {} #[cfg(test)] mod tests { @@ -1914,4 +1948,374 @@ mod tests { ] ); } + + #[test] + fn java_comments() { + check_metrics!( + "for (int i = 0; i < 100; i++) { \ + // Print hello + System.out.println(\"hello\"); \ + // Print world + System.out.println(\"hello\"); \ + }", + "foo.java", + JavaParser, + loc, + [ + (cloc, 2, usize), // The number of comments is 2 + ] + ); + } + + #[test] + fn java_blank() { + check_metrics!( + "int x = 1; + + + int y = 2;", + "foo.java", + JavaParser, + loc, + [ + (blank, 2, usize), // The number of blank lines is 2 + ] + ); + } + + #[test] + fn java_sloc() { + check_metrics!( + "for (int i = 0; i < 100; i++) { + System.out.println(i); + }", + "foo.java", + JavaParser, + loc, + [ + (sloc, 3, usize), // The number of lines is 3 + ] + ); + } + + #[test] + fn java_module_sloc() { + check_metrics!( + "module helloworld{ + exports com.test; + }", + "foo.java", + JavaParser, + loc, + [ + (sloc, 3, usize), // The number of lines is 3 + ] + ); + } + + #[test] + fn java_single_ploc() { + check_metrics!( + "int x = 1;", + "foo.java", + JavaParser, + loc, + [ + (ploc, 1, usize), // The number of code lines is 1 + ] + ); + } + + #[test] + fn java_simple_ploc() { + check_metrics!( + "for (int i = 0; i < 100; i = i++) { + System.out.println(i); + }", + "foo.java", + JavaParser, + loc, + [ + (ploc, 3, usize), // The number of code lines is 3 + ] + ); + } + + #[test] + fn java_multi_ploc() { + check_metrics!( + "int x = 1; + for (int i = 0; i < 100; i++) { + System.out.println(i); + }", + "foo.java", + JavaParser, + loc, + [ + (ploc, 4, usize), // The number of code lines is 4 + ] + ); + } + + #[test] + fn java_single_statement_lloc() { + check_metrics!( + "int max = 10;", + "foo.java", + JavaParser, + loc, + [ + (lloc, 1, usize), // The number of statements is 1 + ] + ); + } + + #[test] + fn java_for_lloc() { + check_metrics!( + "for (int i = 0; i < 100; i++) { // + 1 + System.out.println(i); // + 1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 2, usize), // The number of statements is 2 + ] + ); + } + + #[test] + fn java_foreach_lloc() { + check_metrics!( + " + int arr[]={12,13,14,44}; // +1 + for (int i:arr) { // +1 + System.out.println(i); // +1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 3, usize), // The number of statements is 3 + ] + ); + } + + #[test] + fn java_while_lloc() { + check_metrics!( + " + int i=0; // +1 + while(i < 10) { // +1 + i++; // +1 + System.out.println(i); // +1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 4, usize), // The number of statements is 4 + ] + ); + } + + #[test] + fn java_do_while_lloc() { + check_metrics!( + " + int i=0; // +1 + do { // +1 + i++; // +1 + System.out.println(i); // +1 + } while(i < 10)", + "foo.java", + JavaParser, + loc, + [ + (lloc, 4, usize), // The number of statements is 4 + ] + ); + } + + #[test] + fn java_switch_lloc() { + check_metrics!( + "switch(grade) { // +1 + case 'A' : + System.out.println(\"Pass with distinction\"); // +1 + break; // +1 + case 'B' : + case 'C' : + System.out.println(\"Pass\"); // +1 + break; // +1 + case 'D' : + System.out.println(\"At risk\"); // +1 + case 'F' : + System.out.println(\"Fail\"); // +1 + break; // +1 + default : + System.out.println(\"Invalid grade\"); // +1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 9, usize), // The number of statements is 6 + ] + ); + } + + #[test] + fn java_continue_lloc() { + check_metrics!( + "int max = 10; // +1 + + for (int i = 0; i < max; i++) { // +1 + if(i % 2 == 0) { continue;} + 2 + System.out.println(i); // +1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 5, usize), // The number of statements is 5 + ] + ); + } + + #[test] + fn java_try_lloc() { + check_metrics!( + "try { // +1 + int[] myNumbers = {1, 2, 3}; // +1 + System.out.println(myNumbers[10]); // +1 + } catch (Exception e) { + System.out.println(e.getMessage()); // +1 + throw e; // +1 + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 5, usize), // The number of statements is 5 + ] + ); + } + + #[test] + fn java_class_loc() { + check_metrics!( + " + public class Person { + private String name; + public Person(String name){ + this.name = name; // +1 + } + public String getName() { + return name; // +1 + } + }", + "foo.java", + JavaParser, + loc, + [ + (lloc, 2, usize), // The number of statements is 2 + ] + ); + } + + #[test] + fn java_expressions_lloc() { + check_metrics!( + "int x = 10; // +1 local var declaration + x=+89; // +1 expression statement + int y = x * 2; // +1 local var declaration + IntFunction double = (n) -> n*2; // +1 local var declaration + int y2 = double(x); // +1 local var declaration + System.out.println(\"double \" + x + \" = \" + y2); // +1 expression statement + String message = (x % 2) == 0 ? \"Evenly done.\" : \"Oddly done.\"; // +1 local var declaration + Object done = (Runnable) () -> { System.out.println(\"Done!\"); }; // +2 local var declaration + expression statement + String s = \"string\"; // +1 local var declaration + boolean isS = (s instanceof String); // +1 local var declaration + done.run(); // +1 expression statement + ", + "foo.java", + JavaParser, + loc, + [ + (lloc, 12, usize), // The number of statements is 12 + ] + ); + } + + #[test] + fn java_statement_inline_loc() { + check_metrics!( + "for (int i = 0; i < 100; i++) { System.out.println(\"hello\"); }", + "foo.java", + JavaParser, + loc, + [ + (ploc, 1, usize), // The number of code lines is 1 + (lloc, 2, usize), // The number of statements is 2 + (cloc, 0, usize), // The number of comments is 0 + ] + ); + } + + #[test] + fn java_general_loc() { + check_metrics!( + "int max = 100; + + /* + Loop through and print + from: 0 + to: max + */ + for (int i = 0; i < max; i++) { + // Print the value + System.out.println(i); + }", + "foo.java", + JavaParser, + loc, + [ + (sloc, 11, usize), // The number of lines is 11 + (ploc, 4, usize), // The number of code lines is 4 + (lloc, 3, usize), // The number of statements is 3 + (cloc, 6, usize), // The number of comments is 6 + (blank, 1, usize) // The number of blank lines is 1 + ] + ); + } + + #[test] + fn java_main_class_loc() { + check_metrics!( + "package com.company; + /** + * The HelloWorldApp class implements an application that + * simply prints \"Hello World!\" to standard output. + */ + + class HelloWorldApp { + public void main(String[] args) { + String message = args.length == 0 ? \"Hello empty world\" : \"Hello world\"; // +1 lloc : 1 var assignment + System.out.println(message); // Display the string. +1 lloc + } + }", + "foo.java", + JavaParser, + loc, + [ + (sloc, 12, usize), // The number of lines is 12 + (ploc, 7, usize), // The number of code lines is 7 + (lloc, 2, usize), // The number of statements is 2 + (cloc, 6, usize), // The number of comments is 6 + (blank, 1, usize) // The number of blank lines is 1 + ] + ); + } }