Comments are NAMED nodes in tree-sitter, so a comment interleaved in a * decorator run would end the walk and silently drop every decorator above it: * * @Post('login') <-- lost * @HttpCode(HttpStatus.OK) <-- lost * // why this route is throttled <-- walk stopped here * @Throttle({ ... }) <-- kept * async login(...) * * Documenting a
| 1960 | * comments as transparent — like the anonymous tokens already skipped below. |
| 1961 | * (is_comment_node() is defined above, near the docstring helpers.) */ |
| 1962 | static const char **extract_decorators(CBMArena *a, TSNode node, const char *source, |
| 1963 | CBMLanguage lang, const CBMLangSpec *spec) { |
| 1964 | if (!spec->decorator_node_types || !spec->decorator_node_types[0]) { |
| 1965 | return NULL; |
| 1966 | } |
| 1967 | |
| 1968 | int count = 0; |
| 1969 | TSNode prev = ts_node_prev_sibling(node); |
| 1970 | while (!ts_node_is_null(prev)) { |
| 1971 | if (cbm_kind_in_set(prev, spec->decorator_node_types)) { |
| 1972 | count++; |
| 1973 | } else if (ts_node_is_named(prev) && !is_comment_node(ts_node_type(prev))) { |
| 1974 | /* A real preceding construct ends the decorator run. Anonymous |
| 1975 | * tokens (e.g. TS `export` between `@Decorator` and the |
| 1976 | * `class_declaration`) and comments are skipped so the decorator |
| 1977 | * is still seen. */ |
| 1978 | break; |
| 1979 | } |
| 1980 | prev = ts_node_prev_sibling(prev); |
| 1981 | } |
| 1982 | |
| 1983 | TSNode wrappers[MAX_ATTR_WRAPPERS]; |
| 1984 | int wn = 0; |
| 1985 | int mod_count = 0; |
| 1986 | int child_count = 0; |
| 1987 | if (count == 0) { |
| 1988 | wn = find_jvm_modifiers(node, lang, wrappers, MAX_ATTR_WRAPPERS); |
| 1989 | for (int w = 0; w < wn; w++) { |
| 1990 | mod_count += count_modifier_annotations(wrappers[w], spec); |
| 1991 | } |
| 1992 | /* Languages like Scala attach the annotation directly as a child of the |
| 1993 | * definition node (no wrapper, no prev-sibling). */ |
| 1994 | if (mod_count == 0) { |
| 1995 | child_count = count_child_decorators(node, spec); |
| 1996 | } |
| 1997 | } |
| 1998 | |
| 1999 | int total = count + mod_count + child_count; |
| 2000 | if (total == 0) { |
| 2001 | return NULL; |
| 2002 | } |
| 2003 | |
| 2004 | const char **result = |
| 2005 | (const char **)cbm_arena_alloc(a, sizeof(const char *) * (total + NULL_TERM)); |
| 2006 | if (!result) { |
| 2007 | return NULL; |
| 2008 | } |
| 2009 | |
| 2010 | int idx = 0; |
| 2011 | prev = ts_node_prev_sibling(node); |
| 2012 | while (!ts_node_is_null(prev) && idx < count) { |
| 2013 | if (cbm_kind_in_set(prev, spec->decorator_node_types)) { |
| 2014 | result[idx++] = cbm_node_text(a, prev, source); |
| 2015 | } else if (ts_node_is_named(prev) && !is_comment_node(ts_node_type(prev))) { |
| 2016 | break; |
| 2017 | } |
| 2018 | prev = ts_node_prev_sibling(prev); |
| 2019 | } |
no test coverage detected