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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! `Checker` implementation for Groovy.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
use crate::lang_helpers::doc_comment::{java_doc, node_bytes};
impl Checker for GroovyCode {
fn is_comment(node: &Node) -> bool {
// `groovydoc_comment` is Groovy's `/** … */` Groovydoc kind, a
// distinct grammar node (Groovy 150) from the `//` LineComment
// and `/* */` BlockComment. The `Loc` cloc arm already counts it
// (loc.rs); this restores parity so tokens/find agree too (#697).
matches!(
node.kind_id().into(),
Groovy::LineComment | Groovy::BlockComment | Groovy::GroovydocComment
)
}
fn is_doc_comment(node: &Node, code: &[u8]) -> bool {
// The grammar marks Groovydoc, but its `groovydoc_comment` also
// takes the empty `/**/`, which documents nothing. Groovy's own
// `GroovydocManager` reads any other comment opening `/**`, a
// `/***` one included, exactly as javac does for Javadoc.
node.kind_id() == Groovy::GroovydocComment && java_doc(node_bytes(node, code))
}
// `GroovydocManager::findDocCommentByNode` reads the last comment
// token before a declaration and keeps it only if it opens `/**`, so
// a plain comment in between detaches the Groovydoc; blank rows do
// not. A package declaration resets nothing, so the file's first
// declaration still reads a comment above `package` (#1613).
// The lexer sends a block comment that code follows on its row to the
// hidden channel (`ignoreMultiLineCommentConditionally`), where the
// manager never sees it.
const DOC_BINDING: Option<DocBinding> = Some(DocBinding {
skips: |node, _| node.kind_id() == Groovy::PackageDeclaration,
..DocBinding::new(KeptDocs::Nearest {
hidden: |comment, next| {
comment.kind_id() != Groovy::LineComment && next.start_row() == comment.end_row()
},
})
});
// The nodes `AstBuilder` hands to `GroovydocManager::handle`: a type,
// a method or constructor, a field or property, an enum constant.
fn is_doc_target<'a>(node: &Node<'a>, _code: &[u8], _: Ancestors<'a, '_>) -> bool {
matches!(
node.kind_id().into(),
Groovy::ClassDeclaration
| Groovy::TraitDeclaration
| Groovy::InterfaceDeclaration
| Groovy::EnumDeclaration
| Groovy::RecordDeclaration
| Groovy::AnnotationTypeDeclaration
| Groovy::MethodDeclaration
| Groovy::ConstructorDeclaration
| Groovy::FieldDeclaration
| Groovy::EnumConstant
)
}
// A `static { … }` initializer opens a function space without being
// `is_func`, and Groovydoc reads a doc comment in it no more than one
// in a method body.
fn holds_doc_comment_in_body<'a>(
ancestor: &Node<'a>,
ancestors: Ancestors<'a, '_>,
_below: &Node<'a>,
) -> bool {
Self::is_func(ancestor, ancestors)
|| Self::is_closure(ancestor, ancestors)
|| ancestor.kind_id() == Groovy::StaticInitializer
}
// Mirrors `impl Checker for JavaCode` exactly: `EnumDeclaration`,
// `RecordDeclaration`, and `AnnotationTypeDeclaration` open class
// spaces so `Npa`/`Npm`/`Wmc` walk their bodies. Cross-language parity
// with Java is the point — identical-shape sources must agree on
// class-shaped metrics (issue #280).
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Groovy::SourceFile
| Groovy::ClassDeclaration
| Groovy::TraitDeclaration
| Groovy::InterfaceDeclaration
| Groovy::EnumDeclaration
| Groovy::RecordDeclaration
| Groovy::AnnotationTypeDeclaration
// `static { … }` — see `JavaCode::is_func_space`, which
// this mirrors (#1184).
| Groovy::StaticInitializer
)
}
fn is_func<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
matches!(
node.kind_id().into(),
Groovy::MethodDeclaration | Groovy::ConstructorDeclaration
)
}
// A primitive type is a name kind spelled `int`, `void`, … (#1568),
// which only the bytes can tell, and this predicate gets none. It is
// consulted only for a node `GroovyCode::get_op_type_with_code`
// already billed as an operator, and that bills these two kinds as
// one only for a primitive spelling, so the kind alone suffices to
// key each primitive by its text.
fn is_primitive(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Groovy::Identifier | Groovy::TypeIdentifier
)
}
fn is_closure<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
matches!(node.kind_id().into(), Groovy::Closure)
}
// `command_chain` is the new grammar's distinct node for Groovy's
// command-style juxtaposed calls (`foo bar baz`) which the prior
// amaanq grammar mis-modelled as `juxt_function_call`; it is a
// genuine method-call form and stays in `is_call`.
//
// Intentionally excludes `ObjectCreationExpression` (`new Foo()`):
// `is_call` follows the Java-family convention (Java's `is_call` =
// `MethodInvocation`, C#'s = `InvocationExpression`) of counting
// method/function call sites only. Constructor invocations are an
// ABC concern — Groovy's ABC `branches` counts `New` separately
// (see `groovy_count_token_branch` in metrics/abc.rs).
fn is_call(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Groovy::MethodInvocation | Groovy::CommandChain
)
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Groovy::LPAREN | Groovy::COMMA | Groovy::RPAREN
)
}
impl_simple_is_string!(Groovy, StringLiteral);
// The dekobon Groovy grammar models `if_statement` with the `else`
// keyword token emitted inline followed by the inner `if_statement`
// sibling — same shape as the prior amaanq grammar and Java.
impl_is_else_if_prev_sibling!(Groovy, IfStatement, Else);
}