1use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6pub struct ObjC;
8
9impl Language for ObjC {
10 fn name(&self) -> &'static str {
11 "Objective-C"
12 }
13 fn extensions(&self) -> &'static [&'static str] {
14 &["m", "mm"]
15 }
16 fn grammar_name(&self) -> &'static str {
17 "objc"
18 }
19
20 fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
21 Some(self)
22 }
23
24 fn build_signature(&self, node: &Node, content: &str) -> String {
25 let text = &content[node.byte_range()];
26 text.lines().next().unwrap_or(text).trim().to_string()
27 }
28
29 fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
30 match node.kind() {
31 "preproc_include" => {
32 let text = &content[node.byte_range()];
33 vec![Import {
34 module: text.trim().to_string(),
35 names: Vec::new(),
36 alias: None,
37 is_wildcard: false,
38 is_relative: text.contains('"'),
39 line: node.start_position().row + 1,
40 }]
41 }
42 _ => Vec::new(),
43 }
44 }
45
46 fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
47 if import.is_relative {
49 format!("#import \"{}\"", import.module)
50 } else {
51 format!("#import <{}>", import.module)
52 }
53 }
54
55 fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
56 let name = symbol.name.as_str();
57 match symbol.kind {
58 crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
59 crate::SymbolKind::Module => name == "tests" || name == "test",
60 _ => false,
61 }
62 }
63
64 fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
65 Some(*node)
67 }
68
69 fn analyze_container_body(
70 &self,
71 body_node: &Node,
72 content: &str,
73 inner_indent: &str,
74 ) -> Option<ContainerBody> {
75 let start = body_node.start_byte();
78 let end = body_node.end_byte();
79 let bytes = content.as_bytes();
80
81 let mut content_start = start;
83 while content_start < end && bytes[content_start] != b'\n' {
84 content_start += 1;
85 }
86 if content_start < end {
87 content_start += 1; }
89
90 let mut content_end = end;
92 let mut c = body_node.walk();
93 for child in body_node.children(&mut c) {
94 if child.kind() == "@end" {
95 content_end = child.start_byte();
96 while content_end > content_start
98 && matches!(bytes[content_end - 1], b' ' | b'\t' | b'\n')
99 {
100 content_end -= 1;
101 }
102 break;
103 }
104 }
105
106 let is_empty = content[content_start..content_end].trim().is_empty();
107 Some(ContainerBody {
108 content_start,
109 content_end,
110 inner_indent: inner_indent.to_string(),
111 is_empty,
112 })
113 }
114
115 fn extract_implements(&self, node: &Node, content: &str) -> crate::ImplementsInfo {
116 let mut implements = Vec::new();
117 if let Some(superclass) = node.child_by_field_name("superclass") {
119 implements.push(content[superclass.byte_range()].to_string());
120 }
121 let mut cursor = node.walk();
124 for child in node.children(&mut cursor) {
125 if child.kind() == "parameterized_arguments" {
126 let mut pc = child.walk();
127 for item in child.children(&mut pc) {
128 if item.kind() == "identifier" || item.kind() == "type_identifier" {
129 implements.push(content[item.byte_range()].to_string());
130 } else if item.kind() == "type_name" {
131 let mut tc = item.walk();
133 for inner in item.children(&mut tc) {
134 if inner.kind() == "type_identifier" || inner.kind() == "identifier" {
135 implements.push(content[inner.byte_range()].to_string());
136 }
137 }
138 }
139 }
140 }
141 }
142 crate::ImplementsInfo {
143 is_interface: false,
144 implements,
145 }
146 }
147
148 fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
149 if let Some(n) = node
150 .child_by_field_name("name")
151 .or_else(|| node.child_by_field_name("declarator"))
152 {
153 return Some(&content[n.byte_range()]);
154 }
155 for i in 0..node.child_count() {
157 if let Some(child) = node.child(i as u32)
158 && child.kind() == "identifier"
159 {
160 return Some(&content[child.byte_range()]);
161 }
162 }
163 None
164 }
165}
166
167impl LanguageSymbols for ObjC {}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use crate::validate_unused_kinds_audit;
173
174 #[test]
175 fn unused_node_kinds_audit() {
176 #[rustfmt::skip]
177 let documented_unused: &[&str] = &[
178 "preproc_if", "preproc_elif", "preproc_elifdef", "preproc_function_def",
180 "expression_statement", "return_statement", "break_statement", "continue_statement",
182 "goto_statement", "case_statement", "labeled_statement", "attributed_statement",
183 "try_statement", "catch_clause", "throw_statement",
185 "binary_expression", "unary_expression", "conditional_expression",
187 "call_expression", "subscript_expression", "cast_expression",
188 "comma_expression", "assignment_expression", "update_expression",
189 "compound_literal_expression", "generic_expression",
190 "message_expression", "selector_expression", "encode_expression",
192 "at_expression", "available_expression",
193 "declaration", "declaration_list", "field_declaration_list",
195 "property_declaration", "class_declaration", "atomic_declaration",
196 "protocol_forward_declaration", "qualified_protocol_interface_declaration",
197 "compatibility_alias_declaration",
198 "type_name", "type_identifier", "type_qualifier",
200 "sized_type_specifier", "array_type_specifier", "macro_type_specifier",
201 "typedefed_specifier", "union_specifier", "generic_specifier",
202 "method_definition", "method_identifier", "method_type",
204 "field_identifier", "statement_identifier",
206 "attribute_specifier", "attribute_declaration", "storage_class_specifier",
208 "visibility_specification", "property_attributes_declaration",
209 "protocol_qualifier", "alignas_qualifier", "alignof_expression",
210 "availability_attribute_specifier", "platform",
211 "ms_restrict_modifier", "ms_unaligned_ptr_modifier", "ms_based_modifier",
213 "ms_signed_ptr_modifier", "ms_pointer_modifier", "ms_call_modifier",
214 "ms_declspec_modifier", "ms_unsigned_ptr_modifier", "ms_asm_block",
215 "gnu_asm_expression", "va_arg_expression", "offsetof_expression",
217 "function_declarator", "enumerator", "enumerator_list", "else_clause",
219 "module_import", "abstract_block_pointer_declarator",
220 "extension_expression", "pointer_expression", "parenthesized_expression",
222 "sizeof_expression", "range_expression", "field_expression", "block_literal",
223 "implementation_definition", "struct_declaration", "field_declaration",
225 "parameter_declaration", "linkage_specification",
226 "do_statement", "synchronized_statement", "finally_clause",
227 "typeof_specifier", "type_descriptor", "primitive_type",
229 "preproc_else", "preproc_ifdef",
231 "method_parameter", "block_pointer_declarator", "abstract_function_declarator",
233 "bitfield_clause", "struct_declarator", "gnu_asm_qualifier",
234 "method_declaration",
236 "while_statement",
237 "for_statement",
238 "switch_statement",
239 "if_statement",
240 "compound_statement",
241 ];
242 validate_unused_kinds_audit(&ObjC, documented_unused)
243 .expect("Objective-C unused node kinds audit failed");
244 }
245}