Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion internal/cbm/extract_defs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1450,8 +1450,12 @@ static const char *find_route_path_literal(CBMArena *a, TSNode node, const char

// Extract route path from decorator arguments (first string that starts with /).
static const char *extract_route_path_from_args(CBMArena *a, TSNode args, const char *source) {
/* Every argument is checked. Java and Kotlin put no order on annotation
* attributes, so `path` can sit anywhere in the list. Stopping early left
* a real route unread and formed no Route node. Each argument's own
* subtree walk stays bounded by find_route_path_literal below. */
uint32_t nc = ts_node_named_child_count(args);
for (uint32_t ai = 0; ai < nc && ai < DECORATOR_SCAN_LIMIT; ai++) {
for (uint32_t ai = 0; ai < nc; ai++) {
TSNode arg = ts_node_named_child(args, ai);
/* Spring/Kotlin frequently uses named or array-valued annotation args:
* @RequestMapping(value = ["/internal/v1"])
Expand Down
25 changes: 25 additions & 0 deletions tests/test_edge_types_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,30 @@ TEST(handles_spring_java) {
PASS();
}

/* Spring (Java) — the path attribute may sit anywhere in the annotation.
* Java puts no order on annotation attributes, so `path` after `name`,
* `produces` and `consumes` is ordinary source. The argument scan stopped
* after the third attribute, so the path was never read and no Route node
* formed. A HANDLES count alone cannot catch that, because the class-level
* @RequestMapping still produces one route on its own. */
TEST(handles_spring_java_path_attribute_fourth) {
static const char *routes[] = {"/api/orders", NULL};
static const EtFile f[] = {
{"OrderController.java",
"package com.example;\n\n"
"import org.springframework.web.bind.annotation.RequestMapping;\n"
"import org.springframework.web.bind.annotation.GetMapping;\n\n"
"@RequestMapping(\"/api\")\npublic class OrderController {\n"
" @GetMapping(name = \"listOrders\",\n"
" produces = \"application/json\",\n"
" consumes = \"application/json\",\n"
" path = \"/orders\")\n"
" public String listOrders() {\n"
" return \"orders\";\n }\n}\n"}};
ASSERT_TRUE(et_routes_exact(f, 1, routes));
PASS();
}

/* Spring (Kotlin) — same prefix contract, including Kotlin's named array form
* for class-level RequestMapping values. */
TEST(handles_spring_kotlin) {
Expand Down Expand Up @@ -1578,6 +1602,7 @@ SUITE(edge_types_probe) {
RUN_TEST(handles_fastify_js);
RUN_TEST(handles_gin_go);
RUN_TEST(handles_spring_java);
RUN_TEST(handles_spring_java_path_attribute_fourth);
RUN_TEST(handles_spring_kotlin);
RUN_TEST(handles_jaxrs_java);
RUN_TEST(handles_aspnet_csharp);
Expand Down
Loading