Skip to content

Commit c36b4fb

Browse files
fix(routes): find a route path wherever it sits in a decorator's arguments
Java and Kotlin put no order on annotation attributes, so `path` can appear after `method`, `produces` and `consumes`: @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE, path = "/orders") extract_route_path_from_args stopped after the third argument, so the path was never read and no Route node formed. The endpoint was in the source and not in the graph. Four call sites read this function, so the miss covered plain decorator arguments, Spring mappings, and both JAX-RS paths. The loop now checks every argument. That matches find_drf_kwarg_in_args a few lines below, which walks the same kind of node with no cap. Cost stays small: the loop returns on the first path-shaped string, and each argument's own subtree walk is still bounded by find_route_path_literal. The recursive breadth guard inside find_route_path_literal keeps DECORATOR_SCAN_LIMIT. That loop descends to CBM_DESCENDANT_MAX_DEPTH, so breadth 3 by depth 6 is 729 visits at worst and unbounded breadth there is not bounded at all. The test fails without the change, reporting the class-level "/api" route present and "/api/orders" missing. Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
1 parent 5fbab7b commit c36b4fb

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

internal/cbm/extract_defs.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1450,8 +1450,12 @@ static const char *find_route_path_literal(CBMArena *a, TSNode node, const char
14501450

14511451
// Extract route path from decorator arguments (first string that starts with /).
14521452
static const char *extract_route_path_from_args(CBMArena *a, TSNode args, const char *source) {
1453+
/* Every argument is checked. Java and Kotlin put no order on annotation
1454+
* attributes, so `path` can sit anywhere in the list. Stopping early left
1455+
* a real route unread and formed no Route node. Each argument's own
1456+
* subtree walk stays bounded by find_route_path_literal below. */
14531457
uint32_t nc = ts_node_named_child_count(args);
1454-
for (uint32_t ai = 0; ai < nc && ai < DECORATOR_SCAN_LIMIT; ai++) {
1458+
for (uint32_t ai = 0; ai < nc; ai++) {
14551459
TSNode arg = ts_node_named_child(args, ai);
14561460
/* Spring/Kotlin frequently uses named or array-valued annotation args:
14571461
* @RequestMapping(value = ["/internal/v1"])

tests/test_edge_types_probe.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,30 @@ TEST(handles_spring_java) {
416416
PASS();
417417
}
418418

419+
/* Spring (Java) — the path attribute may sit anywhere in the annotation.
420+
* Java puts no order on annotation attributes, so `path` after `name`,
421+
* `produces` and `consumes` is ordinary source. The argument scan stopped
422+
* after the third attribute, so the path was never read and no Route node
423+
* formed. A HANDLES count alone cannot catch that, because the class-level
424+
* @RequestMapping still produces one route on its own. */
425+
TEST(handles_spring_java_path_attribute_fourth) {
426+
static const char *routes[] = {"/api/orders", NULL};
427+
static const EtFile f[] = {
428+
{"OrderController.java",
429+
"package com.example;\n\n"
430+
"import org.springframework.web.bind.annotation.RequestMapping;\n"
431+
"import org.springframework.web.bind.annotation.GetMapping;\n\n"
432+
"@RequestMapping(\"/api\")\npublic class OrderController {\n"
433+
" @GetMapping(name = \"listOrders\",\n"
434+
" produces = \"application/json\",\n"
435+
" consumes = \"application/json\",\n"
436+
" path = \"/orders\")\n"
437+
" public String listOrders() {\n"
438+
" return \"orders\";\n }\n}\n"}};
439+
ASSERT_TRUE(et_routes_exact(f, 1, routes));
440+
PASS();
441+
}
442+
419443
/* Spring (Kotlin) — same prefix contract, including Kotlin's named array form
420444
* for class-level RequestMapping values. */
421445
TEST(handles_spring_kotlin) {
@@ -1578,6 +1602,7 @@ SUITE(edge_types_probe) {
15781602
RUN_TEST(handles_fastify_js);
15791603
RUN_TEST(handles_gin_go);
15801604
RUN_TEST(handles_spring_java);
1605+
RUN_TEST(handles_spring_java_path_attribute_fourth);
15811606
RUN_TEST(handles_spring_kotlin);
15821607
RUN_TEST(handles_jaxrs_java);
15831608
RUN_TEST(handles_aspnet_csharp);

0 commit comments

Comments
 (0)