chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* repro_issue382.c — Reproduce-first case for OPEN bug #382.
|
||||
*
|
||||
* Bug #382: "Java: @Annotation, signatures, and all AST properties missing
|
||||
* from graph nodes"
|
||||
*
|
||||
* Root cause (confirmed by maintainer + reporter re-open):
|
||||
* extract_decorators() in internal/cbm/extract_defs.c first scans
|
||||
* ts_node_prev_sibling() looking for nodes of type "annotation" /
|
||||
* "marker_annotation". In the Java AST emitted by tree-sitter-java, those
|
||||
* nodes are NOT prev-siblings of either the class_declaration or the
|
||||
* method_declaration — they live INSIDE the node's own `modifiers` child:
|
||||
*
|
||||
* class_declaration
|
||||
* modifiers
|
||||
* marker_annotation <- @Entity
|
||||
* marker_annotation <- @RestController
|
||||
* type_identifier: "User"
|
||||
* class_body
|
||||
* method_declaration
|
||||
* modifiers
|
||||
* marker_annotation <- @Override
|
||||
* annotation <- @GetMapping("/users")
|
||||
* type_identifier: "String"
|
||||
* ...
|
||||
*
|
||||
* The code does have a fallback that calls find_jvm_modifiers() to search
|
||||
* the `modifiers` child when prev-sibling count == 0, which covers the
|
||||
* simple @GetMapping-on-method case already tested in test_extraction.c
|
||||
* (extract_java_method_annotations_issue382, which passes green on v0.7.0).
|
||||
*
|
||||
* What is NOT covered by that existing test:
|
||||
* a) CLASS-LEVEL annotations (@Entity, @RestController) on the class node
|
||||
* itself — the existing test only extracts Method nodes; it never
|
||||
* checks the Class node's .decorators.
|
||||
* b) marker_annotation (no-arg form, e.g. @Override, @Entity) on methods
|
||||
* — the existing test uses @GetMapping("/x") which is a full
|
||||
* `annotation` node with arguments and does a substring match against
|
||||
* the whole text "@GetMapping(\"/x\")". marker_annotations have a
|
||||
* different tree-sitter node type and are historically mis-counted.
|
||||
* c) Multiple stacked annotations on a single method/class.
|
||||
*
|
||||
* These cases regress when the fallback path is absent or broken (e.g. the
|
||||
* fix only wired the method path, not the class path, or it works for
|
||||
* `annotation` nodes but not `marker_annotation`).
|
||||
*
|
||||
* Expected (correct) behaviour:
|
||||
* - The Class def for "User" carries decorators:
|
||||
* decorators[0] contains "Entity"
|
||||
* decorators[1] contains "RestController" (or vice-versa)
|
||||
* - The Method def for "getUser" carries decorators:
|
||||
* at least one entry contains "Override"
|
||||
* at least one entry contains "GetMapping"
|
||||
* - method "getUser" has a non-empty signature.
|
||||
*
|
||||
* Actual (buggy) behaviour:
|
||||
* - Class def for "User": decorators == NULL (no annotations extracted)
|
||||
* - Method def for "getUser": marker_annotation @Override is dropped;
|
||||
* decorators may be NULL or miss @Override.
|
||||
* → assertions below are RED on current code if either path is broken.
|
||||
*
|
||||
* Why this is STRONGER than the existing test_extraction.c #382 reference:
|
||||
* 1. It asserts decorators on the CLASS node — never checked before.
|
||||
* 2. It specifically asserts that a marker_annotation (@Override, @Entity)
|
||||
* is captured, not just a full annotation with arguments.
|
||||
* 3. It asserts BOTH annotations on a multi-annotated class, exercising the
|
||||
* count loop that must find > 1 entry.
|
||||
* 4. It uses ASSERT_NOT_NULL(m->decorators) before touching decorators[i],
|
||||
* so a NULL decorators field fails loudly rather than crashing/skipping.
|
||||
*/
|
||||
|
||||
#include "test_framework.h"
|
||||
#include "cbm.h"
|
||||
|
||||
/* Convenience: extract one file, return result (caller frees). */
|
||||
static CBMFileResult *rx(const char *src, CBMLanguage lang,
|
||||
const char *proj, const char *path) {
|
||||
return cbm_extract_file(src, (int)strlen(src), lang, proj, path,
|
||||
0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Return the first definition whose label AND name both match (either may be
|
||||
* NULL to wildcard). Mirrors the helper in repro_extraction.c. */
|
||||
static CBMDefinition *find_def(CBMFileResult *r, const char *label,
|
||||
const char *name) {
|
||||
for (int i = 0; i < r->defs.count; i++) {
|
||||
CBMDefinition *d = &r->defs.items[i];
|
||||
if (label && (!d->label || strcmp(d->label, label) != 0))
|
||||
continue;
|
||||
if (name && (!d->name || strcmp(d->name, name) != 0))
|
||||
continue;
|
||||
return d;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return 1 if any entry in the NULL-terminated decorators array contains
|
||||
* needle as a substring. */
|
||||
static int decorators_contain(const CBMDefinition *d, const char *needle) {
|
||||
if (!d || !d->decorators)
|
||||
return 0;
|
||||
for (int i = 0; d->decorators[i]; i++) {
|
||||
if (strstr(d->decorators[i], needle))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ───────────────────────────────────────────────────────────────────
|
||||
* repro_issue382_java_annotations_on_nodes
|
||||
*
|
||||
* Asserts that BOTH the Class node AND the Method node produced by
|
||||
* cbm_extract_file carry their Java annotations in .decorators:
|
||||
*
|
||||
* @Entity
|
||||
* @RestController
|
||||
* public class User {
|
||||
* @Override
|
||||
* @GetMapping("/users")
|
||||
* public String getUser(String id) { return id; }
|
||||
* }
|
||||
*
|
||||
* RED if:
|
||||
* • The Class "User" has decorators == NULL (class-level annots dropped)
|
||||
* • The Class "User" decorators do not contain "Entity"
|
||||
* • The Class "User" decorators do not contain "RestController"
|
||||
* • The Method "getUser" has decorators == NULL (method-level annots dropped)
|
||||
* • The Method "getUser" decorators do not contain "Override" ← marker_annotation
|
||||
* • The Method "getUser" decorators do not contain "GetMapping" ← annotation
|
||||
* • The Method "getUser" has NULL or empty signature
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
TEST(repro_issue382_java_annotations_on_nodes) {
|
||||
CBMFileResult *r = rx(
|
||||
"@Entity\n"
|
||||
"@RestController\n"
|
||||
"public class User {\n"
|
||||
" @Override\n"
|
||||
" @GetMapping(\"/users\")\n"
|
||||
" public String getUser(String id) { return id; }\n"
|
||||
"}\n",
|
||||
CBM_LANG_JAVA, "t", "User.java");
|
||||
|
||||
ASSERT_NOT_NULL(r);
|
||||
ASSERT_FALSE(r->has_error);
|
||||
|
||||
/* ── Class node: two class-level marker_annotations ── */
|
||||
CBMDefinition *cls = find_def(r, "Class", "User");
|
||||
ASSERT_NOT_NULL(cls);
|
||||
|
||||
/* The Class def MUST carry a non-NULL decorators array.
|
||||
* RED if class-level annotations are silently dropped. */
|
||||
ASSERT_NOT_NULL(cls->decorators);
|
||||
|
||||
/* @Entity (marker_annotation) must be present on the Class. */
|
||||
ASSERT_TRUE(decorators_contain(cls, "Entity"));
|
||||
|
||||
/* @RestController (marker_annotation) must also be present. */
|
||||
ASSERT_TRUE(decorators_contain(cls, "RestController"));
|
||||
|
||||
/* ── Method node: one marker_annotation + one annotation ── */
|
||||
CBMDefinition *method = find_def(r, "Method", "getUser");
|
||||
ASSERT_NOT_NULL(method);
|
||||
|
||||
/* Method decorators must be non-NULL. */
|
||||
ASSERT_NOT_NULL(method->decorators);
|
||||
|
||||
/* @Override is a marker_annotation (no argument list) — historically
|
||||
* the most likely to be missed if the extractor only handles the
|
||||
* `annotation` node type but not `marker_annotation`. */
|
||||
ASSERT_TRUE(decorators_contain(method, "Override"));
|
||||
|
||||
/* @GetMapping("/users") is a full annotation (with argument) — this is
|
||||
* what the existing test_extraction.c case checks; include it here too
|
||||
* so we catch any regression. */
|
||||
ASSERT_TRUE(decorators_contain(method, "GetMapping"));
|
||||
|
||||
/* Signature must be extracted: Java method_declaration has a `parameters`
|
||||
* field that the extractor reads into def.signature. */
|
||||
ASSERT_NOT_NULL(method->signature);
|
||||
ASSERT_TRUE(method->signature[0] != '\0');
|
||||
|
||||
cbm_free_result(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── Suite ──────────────────────────────────────────────────────── */
|
||||
SUITE(repro_issue382) {
|
||||
RUN_TEST(repro_issue382_java_annotations_on_nodes);
|
||||
}
|
||||
Reference in New Issue
Block a user