Skip to main content

big_code_analysis_ast/getter/
tsx.rs

1//! `Getter` implementation for TSX.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5
6impl Getter for TsxCode {
7    fn get_space_kind(node: &Node) -> SpaceKind {
8        use Tsx::*;
9
10        match node.kind_id().into() {
11            // `ClassStaticBlock` is ES2022 `static { … }` (#1184).
12            FunctionExpression
13            | MethodDefinition
14            | GeneratorFunction
15            | FunctionDeclaration
16            | GeneratorFunctionDeclaration
17            | ArrowFunction
18            | ClassStaticBlock => SpaceKind::Function,
19            Class | ClassDeclaration | AbstractClassDeclaration => SpaceKind::Class,
20            InterfaceDeclaration => SpaceKind::Interface,
21            Program => SpaceKind::Unit,
22            _ => SpaceKind::Unknown,
23        }
24    }
25
26    fn get_func_space_name<'a, 'tree>(
27        node: &Node<'tree>,
28        code: &'a [u8],
29        ancestors: Ancestors<'tree, '_>,
30    ) -> Option<&'a str> {
31        // A class static block has no name token and no naming parent to
32        // fall back on, so it would otherwise land on `<anonymous>`
33        // alongside every arrow and function expression (#1184).
34        if node.kind_id() == Tsx::ClassStaticBlock as u16 {
35            return Some("<static-init>");
36        }
37        if let Some(name) = node.child_by_field_name("name") {
38            return node_text(code, &name);
39        }
40        // Otherwise the name comes from the binding site: a pair
41        // (`foo: function () {}`) or a variable declaration
42        // (`var aFun = function () {}`). The two differ only in which
43        // field carries the name, so they collapse to one lookup.
44        let bound_name = ancestors.parent(node).and_then(|parent| {
45            let field = match parent.kind_id().into() {
46                Tsx::Pair => "key",
47                Tsx::VariableDeclarator => "name",
48                _ => return None,
49            };
50            parent.child_by_field_name(field)
51        });
52        bound_name.map_or(Some("<anonymous>"), |name| node_text(code, &name))
53    }
54
55    // TSX exposes two anonymous `"string"` aliases: `String2` (kind_id
56    // 261, the string-literal alias — ordinary and JSX-attribute
57    // literals both parse as it), which is an operand like any other
58    // string literal, and `String3` (kind_id 141, the `: string` type
59    // keyword — the role TS's `String2` plays, emitted only as the
60    // child of a `predefined_type` wrapper). `String3` is deliberately
61    // NOT an operand: the wrapper already counts as the text-keyed
62    // `"string"` operator, so #313's listing of the child too counted
63    // one source token as operator AND operand while `: number` /
64    // `: boolean` counted once (#1261; see the TS invocation).
65    //
66    // TSX's TS-only member-expression productions `NestedIdentifier`
67    // and `MemberExpression4` were operand extras until #1263 and are
68    // now deliberately absent, matching the `MemberExpression*` drop in
69    // the macro body: a member access or a `namespace N.M` header
70    // contributes its identifier leaves plus the `.` operator, never
71    // the composite text as well.
72    //
73    // `LTSLASH` / `SLASHGT` are the JSX closing and self-closing tag
74    // delimiters. TSX is the only one of the two TypeScript grammars
75    // with JSX, so the `.ts` invocation has no such variants to pass
76    // (#1395).
77    impl_js_family_get_op_type!(
78        Tsx,
79        op_extras: [QMARKDOT, PredefinedType, LTSLASH, SLASHGT],
80        operand_extras: [Identifier2, String2],
81        predefined_void: PredefinedType,
82    );
83
84    get_operator!(Tsx);
85}