Skip to main content

big_code_analysis_ast/getter/
objc.rs

1//! `Getter` implementation for Objective-C.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5use crate::c_declarator::declarator_name;
6
7impl Getter for ObjcCode {
8    fn get_func_space_name<'a, 'tree>(
9        node: &Node<'tree>,
10        code: &'a [u8],
11        _ancestors: Ancestors<'tree, '_>,
12    ) -> Option<&'a str> {
13        // Issue #285 contract: every `Objc::FunctionDefinition*` alias
14        // must be enumerated here AND in `get_space_kind` below AND in
15        // `is_func` / `is_func_space` (see `src/checker.rs`). Free
16        // functions reach their name through the C declarator chain; the
17        // ObjC containers (`method_definition`, `class_interface` /
18        // `class_implementation`, `protocol_declaration`) carry no `name`
19        // field, so their name is the first `identifier` child — the
20        // class / protocol name, or a method's first selector keyword.
21        match node.kind_id().into() {
22            Objc::FunctionDefinition | Objc::FunctionDefinition2 => {
23                // The name is not a child of the function node — see
24                // `crate::c_declarator` (#1208).
25                if let Some(name) = declarator_name::<Self>(node)
26                    && matches!(
27                        name.kind_id().into(),
28                        Objc::TypeIdentifier | Objc::Identifier | Objc::FieldIdentifier
29                    )
30                {
31                    return node_text(code, &name);
32                }
33            }
34            Objc::MethodDefinition
35            | Objc::ClassInterface
36            | Objc::ClassImplementation
37            | Objc::ProtocolDeclaration => {
38                if let Some(ident) = node.first_child(|id| Objc::Identifier == id) {
39                    return node_text(code, &ident);
40                }
41            }
42            _ => {
43                if let Some(name) = node.child_by_field_name("name") {
44                    return node_text(code, &name);
45                }
46            }
47        }
48        None
49    }
50
51    fn get_space_kind(node: &Node) -> SpaceKind {
52        use Objc::*;
53
54        // `@interface` / `@protocol` declare members without bodies →
55        // `Interface`; `@implementation` carries the method bodies →
56        // `Class`; free functions and `method_definition`s are
57        // `Function` spaces. ObjC blocks (`^{ … }`) are closures counted
58        // by `nom` rather than their own space, mirroring the C++ lambda
59        // (so they fall through to `Unknown` here). Keep the
60        // `FunctionDefinition*` aliases listed (#285).
61        match node.kind_id().into() {
62            FunctionDefinition | FunctionDefinition2 | MethodDefinition => SpaceKind::Function,
63            ClassImplementation => SpaceKind::Class,
64            ClassInterface | ProtocolDeclaration => SpaceKind::Interface,
65            TranslationUnit => SpaceKind::Unit,
66            _ => SpaceKind::Unknown,
67        }
68    }
69
70    fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
71        use Objc::*;
72
73        // ObjC is C plus message sends, blocks, and the `@`-directives.
74        // The operator alphabet is therefore the C set (`src/getter.rs`
75        // `impl Getter for CCode`) extended with the ObjC structural
76        // keywords: fast-enumeration `in`, the boxing/`@`-literal marker
77        // `@`, the `@try` / `@catch` / `@finally` / `@throw` /
78        // `@synchronized` / `@autoreleasepool` control keywords, and the
79        // `@selector` / `@encode` compile-time directives. Each keeps a
80        // distinct kind_id, so keying by kind_id keeps them distinct in n1.
81        // `LPAREN2` is a defensive arm (collapsed to `LPAREN` before
82        // `kind_id()`; #768, see the Cpp note).
83        match node.kind_id().into() {
84            // `@"…"` is one `string_literal` holding its `@` as a child
85            // (tree-sitter-objc 3.0.2), unlike `@42` / `@[…]` / `@{…}`,
86            // where the `@` is a child of the `at_expression` /
87            // `array_literal` / `dictionary_literal` it boxes. The
88            // literal is already the operand, keyed by its whole text,
89            // so billing the marker as an operator too paid the same
90            // byte twice and planted a phantom `@` in n1 for a file
91            // whose only `@` was in NSString literals (grammar-dispatch
92            // §5, the compound-leaf guard).
93            AT if ancestors.parent_has_kind(node, StringLiteral as u16) => TokenRole::Unknown,
94            // The C operator set, then the ObjC-specific structural
95            // keywords / markers from `In` onwards.
96            DOT | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return | Break
97            | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do | EQ
98            | AMPAMP | PIPEPIPE | DASH | DASHDASH | DASHGT | PLUS | PLUSPLUS | SLASH | PERCENT
99            | PIPE | AMP | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | PLUSEQ
100            | DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
101            | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | PrimitiveType | TypeSpecifier
102            | Sizeof | Signed | Unsigned | Long | Short | In | AT | ATtry | ATcatch | ATfinally
103            | ATthrow | ATsynchronized | ATautoreleasepool | ATselector | ATencode => {
104                TokenRole::Operator
105            }
106            // `CharLiteral` — the full derivation lives on the same arm
107            // in `src/getter/c.rs` (#1316): the wrapper is the only
108            // classified node in a character literal, so it bills one
109            // operand per literal, keyed by text, and `Checker::is_string`
110            // deliberately stays without a `CharLiteral` arm.
111            Identifier | TypeIdentifier | FieldIdentifier | StringLiteral | CharLiteral
112            | NumberLiteral | True | False | Null | DOTDOTDOT => TokenRole::Operand,
113            _ => TokenRole::Unknown,
114        }
115    }
116
117    get_operator!(Objc);
118}