| 1798 | } |
| 1799 | |
| 1800 | static const char **extract_decorators(CBMArena *a, TSNode node, const char *source, |
| 1801 | CBMLanguage lang, const CBMLangSpec *spec) { |
| 1802 | if (!spec->decorator_node_types || !spec->decorator_node_types[0]) { |
| 1803 | return NULL; |
| 1804 | } |
| 1805 | |
| 1806 | int count = 0; |
| 1807 | TSNode prev = ts_node_prev_sibling(node); |
| 1808 | while (!ts_node_is_null(prev)) { |
| 1809 | if (cbm_kind_in_set(prev, spec->decorator_node_types)) { |
| 1810 | count++; |
| 1811 | } else if (ts_node_is_named(prev)) { |
| 1812 | /* A real preceding construct ends the decorator run. Anonymous |
| 1813 | * tokens (e.g. TS `export` between `@Decorator` and the |
| 1814 | * `class_declaration`) are skipped so the decorator is still seen. */ |
| 1815 | break; |
| 1816 | } |
| 1817 | prev = ts_node_prev_sibling(prev); |
| 1818 | } |
| 1819 | |
| 1820 | TSNode modifiers = {0}; |
| 1821 | int mod_count = 0; |
| 1822 | int child_count = 0; |
| 1823 | if (count == 0) { |
| 1824 | modifiers = find_jvm_modifiers(node, lang); |
| 1825 | if (!ts_node_is_null(modifiers)) { |
| 1826 | mod_count = count_modifier_annotations(modifiers, spec); |
| 1827 | } |
| 1828 | /* Languages like Scala attach the annotation directly as a child of the |
| 1829 | * definition node (no wrapper, no prev-sibling). */ |
| 1830 | if (mod_count == 0) { |
| 1831 | child_count = count_child_decorators(node, spec); |
| 1832 | } |
| 1833 | } |
| 1834 | |
| 1835 | int total = count + mod_count + child_count; |
| 1836 | if (total == 0) { |
| 1837 | return NULL; |
| 1838 | } |
| 1839 | |
| 1840 | const char **result = |
| 1841 | (const char **)cbm_arena_alloc(a, sizeof(const char *) * (total + NULL_TERM)); |
| 1842 | if (!result) { |
| 1843 | return NULL; |
| 1844 | } |
| 1845 | |
| 1846 | int idx = 0; |
| 1847 | prev = ts_node_prev_sibling(node); |
| 1848 | while (!ts_node_is_null(prev) && idx < count) { |
| 1849 | if (cbm_kind_in_set(prev, spec->decorator_node_types)) { |
| 1850 | result[idx++] = cbm_node_text(a, prev, source); |
| 1851 | } else if (ts_node_is_named(prev)) { |
| 1852 | break; |
| 1853 | } |
| 1854 | prev = ts_node_prev_sibling(prev); |
| 1855 | } |
| 1856 | if (!ts_node_is_null(modifiers)) { |
| 1857 | idx = collect_modifier_decorators(a, modifiers, source, spec, result, idx, total); |
no test coverage detected