1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! `Loc` implementation for Groovy.
#![allow(
clippy::enum_glob_use,
clippy::match_same_arms,
clippy::struct_field_names,
clippy::wildcard_imports
)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use super::*;
impl Loc for GroovyCode {
fn compute(node: &Node, stats: &mut Stats, is_func_space: bool, is_unit: bool) {
use Groovy::*;
let (start, end) = init(node, stats, is_func_space, is_unit);
let kind_id: Groovy = node.kind_id().into();
// LLOC counts statements only — same definition as Java.
// Groovy additions over Java's list:
// - `YieldStatement` for the Java-14+ switch-expression form.
// - `CommandChain` for Groovy's parens-less call as a top-
// level statement (the dekobon grammar's distinct node;
// the prior amaanq grammar called this `juxt_function_call`).
// - `ForInStatement` (the dekobon grammar's name for the
// `for (x in xs)` / `for (Foo x : xs)` shape; the prior
// amaanq grammar called this `enhanced_for_statement`).
// - `PipelineStatement` is the dekobon grammar's distinct
// node for a Jenkinsfile `pipeline { … }` block, treated
// here as a single statement.
match kind_id {
SourceFile => {}
LineComment | BlockComment | GroovydocComment => {
add_cloc_lines(stats, start, end);
}
// A Groovy triple-quoted string (`"""…"""` / `'''…'''`) is a
// `string_literal` spanning several rows; credit every spanned row
// to PLOC to match Python's #415 decision (#778).
StringLiteral => {
add_multiline_string_ploc(node, stats, start, end);
}
// An `ExpressionStatement` whose only child is a bare
// `Closure` is a Groovy-specific grammar artifact: the
// alternative branch of `if (x) { … } else { … }` wraps
// the brace-block as `expression_statement (closure)`
// even though the user wrote it as part of the surrounding
// `if`. Skipping the wrapper avoids double-counting the
// else-branch as a separate LLOC. Real expression
// statements like `expression_statement (command_chain)`
// for `println(x)` keep firing because their child is not
// a bare `Closure`.
ExpressionStatement if node.child(0).is_some_and(|c| c.kind_id() == Closure) => {
// No-op: do not count as LLOC.
}
AssertStatement | BreakStatement | CommandChain | ContinueStatement
| DoWhileStatement | ExpressionStatement | ForInStatement | ForStatement
| IfStatement | PipelineStatement | ReturnStatement | SwitchExpression
| ThrowStatement | TryStatement | WhileStatement | YieldStatement => {
stats.lloc.logical_lines += 1;
}
LocalVariableDeclaration => {
if node.count_specific_ancestors::<GroovyCode>(
|node| node.kind_id() == ForStatement,
|node| node.kind_id() == Block,
) == 0
{
// Skip the initializer slot of a classic `for` loop —
// same reason as Java's impl.
stats.lloc.logical_lines += 1;
}
}
_ => {
check_comment_ends_on_code_line(stats, start);
stats.ploc.lines.insert(start);
}
}
}
}