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
//! `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
}
impl_doxygen_doc_comment!();
// Objective-C adds `@interface`, `@implementation`, `@protocol`,
// `@property`, methods and instance variables; `@class C;`,
// `@protocol P;`, `@synthesize` and `@dynamic` take none (#1613).
impl_doxygen_doc_binding!();
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);
// Besides the primitive types, an `identifier` the getter bills is a
// property attribute word (`nonatomic`, `copy`), whose kind is no
// keyword, so keying it by text lists each word. The `nullable`
// type qualifier is keyed by text too, so it and the `nullable`
// property attribute share one `n1` entry rather than one per map
// (#1579).
#[inline]
fn is_primitive(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Objc::PrimitiveType | Objc::Identifier | Objc::Nullable
)
}
}