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
//! `Checker` implementation for Objective-C.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
impl Checker for ObjcCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Objc::Comment
}
fn is_useful_comment<'a>(node: &Node<'a>, code: &[u8], _ancestors: Ancestors<'a, '_>) -> bool {
get_aho_corasick_match(&code[node.start_byte()..node.end_byte()])
}
// Objective-C builds on C, so the code spaces are the translation
// unit, free functions, and ObjC-specific containers: `@interface`
// / `@protocol` (declarations only → `Interface`), `@implementation`
// (method bodies → `Class`), and method definitions. `BlockLiteral`
// (`^{ … }`) is a closure counted by `nom`, not its own space —
// mirroring how the C++ checker treats `LambdaExpression`. Keep this
// list in sync with `is_func`, `get_func_space_name`, and
// `get_space_kind` (see `src/getter.rs`); the `FunctionDefinition2`
// alias must ride alongside the primary or it is silently dropped
// from FuncSpace creation (#285, lesson 2).
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Objc::TranslationUnit
| Objc::FunctionDefinition
| Objc::FunctionDefinition2
| Objc::MethodDefinition
| Objc::ClassInterface
| Objc::ClassImplementation
| Objc::ProtocolDeclaration
)
}
// Keep in sync with `is_func_space` and the ObjC getters (#285).
// A `method_definition` is the `@implementation`-side method with a
// body; the `@interface`-side `method_declaration` has no body and
// is therefore not a function.
fn is_func<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
matches!(
node.kind_id().into(),
Objc::FunctionDefinition | Objc::FunctionDefinition2 | Objc::MethodDefinition
)
}
// A plain C function written inside `@implementation` / `@interface`
// / `@protocol` is a file-static helper, not a method: no receiver,
// absent from the method table, unsendable. `npm` already declines
// to count it (`src/metrics/npm/objc.rs` counts `method_definition`),
// so `wmc` must not weight it into the container either (#1356) —
// the C++ `friend` divergence of #1301 in a sibling language.
//
// The node kind is the whole predicate, because an Objective-C
// method is *always* a `method_definition`. Neither shape of parent
// check that worked for C++ transfers:
//
// - `implementation_definition` wraps `function_definition` and
// `method_definition` alike, so a parent check alone answers
// `true` for every real method and zeroes the class's WMC
// (`.claude/rules/grammar-dispatch.md` §6).
// - A parent-kind *list* cannot be completed. `@interface` and
// `@protocol` take `function_definition` as a direct child with no
// wrapper, and both also admit `preproc_if` — so a helper inside
// `#if` has a parent kind it shares with a file-scope `#if`.
//
// `true` at file scope is deliberate and inert: `wmc::Stats::merge`
// credits a `Function` child only to a `Class` or `Interface`
// parent. Both aliases ride together per the #285 contract this file
// follows in `is_func` / `is_func_space`.
fn is_non_member_function<'a>(
node: &Node<'a>,
_code: &[u8],
_ancestors: Ancestors<'a, '_>,
) -> bool {
matches!(
node.kind_id().into(),
Objc::FunctionDefinition | Objc::FunctionDefinition2
)
}
// ObjC blocks `^{ … }` are the language's closures.
fn is_closure<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
node.kind_id() == Objc::BlockLiteral
}
// A C `call_expression` (aliased to `CallExpression2`) or an ObjC
// message send `[receiver method]` are both calls.
fn is_call(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Objc::CallExpression | Objc::CallExpression2 | Objc::MessageExpression
)
}
// C's `(void)` marker, shared with C++, Mozcpp and Objective-C.
fn is_empty_param_marker(param: &Node, code: &[u8]) -> bool {
c_family_void_parameter(param, code)
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Objc::LPAREN | Objc::LPAREN2 | Objc::COMMA | Objc::RPAREN
)
}
// ObjC has no raw string literals; `@"…"` parses as `AT` + a plain
// `string_literal`, so the same string kinds as C apply.
impl_simple_is_string!(Objc, StringLiteral, ConcatenatedString);
impl_is_else_if_parent_clause!(Objc, IfStatement, ElseClause);
#[inline]
fn is_primitive(node: &Node) -> bool {
node.kind_id() == Objc::PrimitiveType
}
}