mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
doc-links(php): a bare name() in a type's doc is the type's method
phpDocumentor reads `@see name()` as a function. Authors write it for the
documented class's own method: symfony has 97 such references that ended as
external rows (Command `@see setCode()`, ProgressBar `{@see resumeAll()}`),
and one that bound a global build-script function (ProcessHelper `@see
run()`, meaning its own run()). In the doc of a type or of one of its
members, a bare name() is now a method the type declares or inherits; a
`use function` import of the name still wins, and outside a type the
function rules stay. The ambiguity guard this replaces is gone.
Proof (ship build, Apple M5 Pro / 48 GB): symfony 9917e115 +91 edges
(external bare name() rows 97 -> 6), cakephp a75828db +6 (7 -> 1); a sample
of 14 new edges read against their lines: all correct, inherited methods
included (UploadedFile -> File::guessExtension).
Test: doc_mentions_php_current_class (the method wins over a global function
of the same name; the documented method itself is a self-mention), red with
the rule reverted.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
2fdcbfe288
commit
c5c400a1cb
2 files changed
+33
-28
No files matched your search
@@ -14,10 +14,10 @@
|
||||
* go beyond that tool, as PHP itself has them: an unqualified function name
|
||||
* is taken from a `use function` import, else from the current namespace,
|
||||
* else from the global namespace; and `self` and `static` name the type the
|
||||
* doc sits in, `parent` that type's superclass. A bare `name()` that would
|
||||
* bind a function but is written in a type that declares or inherits a
|
||||
* method of that name is ambiguous: the method or the function, and the
|
||||
* reference does not say which. Type, function
|
||||
* doc sits in, `parent` that type's superclass. Beyond both, as authors
|
||||
* write it: a bare `name()` in the doc of a type (or of its members) that
|
||||
* declares or inherits a method of that name is that method; a `use
|
||||
* function` import of the name still wins. Type, function
|
||||
* and method names compare without case, properties and constants with it.
|
||||
* The global fallback cannot see a function a dependency declares in the
|
||||
* current namespace: a reference to one binds the global function of that
|
||||
@@ -833,17 +833,6 @@ static pr_res_t pr_find_func(const pr_ctx_t *c, const char *fqn, size_t len) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* True when the type the doc sits in declares or inherits a method named
|
||||
* like the reference. */
|
||||
static bool pr_type_has_method(const pr_ctx_t *c, const pr_ref_t *r) {
|
||||
if (c->cur < 0) {
|
||||
return false;
|
||||
}
|
||||
pr_search_t s = {
|
||||
.c = c, .want = 'm', .name = r->cls, .len = r->cls_len, .owner = CBM_NOT_FOUND};
|
||||
return pr_search(&s, c->cur, 0);
|
||||
}
|
||||
|
||||
static pr_res_t pr_function(const pr_ctx_t *c, const pr_ref_t *r) {
|
||||
char fqn[PR_NAME_MAX];
|
||||
if (r->cls[0] == '\\' || memchr(r->cls, '\\', r->cls_len)) {
|
||||
@@ -868,6 +857,14 @@ static pr_res_t pr_function(const pr_ctx_t *c, const pr_ref_t *r) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (c->cur >= 0) {
|
||||
/* a method the documented type declares or inherits */
|
||||
pr_search_t s = {
|
||||
.c = c, .want = 'm', .name = r->cls, .len = r->cls_len, .owner = CBM_NOT_FOUND};
|
||||
if (pr_search(&s, c->cur, 0)) {
|
||||
return pr_member_node(c, &s);
|
||||
}
|
||||
}
|
||||
pr_res_t res = pr_mk(PR_NONE);
|
||||
if (c->ns_len) {
|
||||
int n = snprintf(fqn, sizeof(fqn), "%.*s\\%.*s", (int)c->ns_len, c->ns, (int)r->cls_len,
|
||||
@@ -877,15 +874,7 @@ static pr_res_t pr_function(const pr_ctx_t *c, const pr_ref_t *r) {
|
||||
if (res.kind == PR_NONE) {
|
||||
res = pr_find_func(c, r->cls, r->cls_len); /* PHP's global fallback */
|
||||
}
|
||||
if (res.kind == PR_NONE) {
|
||||
return pr_mk(PR_EXTERNAL); /* a built-in function */
|
||||
}
|
||||
if ((res.kind == PR_NODE || res.kind == PR_GAP) && pr_type_has_method(c, r)) {
|
||||
/* written in a type that has a method of that name: the method or
|
||||
* this function, and the reference does not pick one */
|
||||
return pr_mk(PR_AMBIGUOUS);
|
||||
}
|
||||
return res;
|
||||
return res.kind == PR_NONE ? pr_mk(PR_EXTERNAL) : res; /* none: a built-in */
|
||||
}
|
||||
|
||||
/* ── Outcomes ────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -216,6 +216,7 @@ static void php_write_repo(const char *repo) {
|
||||
"function global_helper() {}\n"
|
||||
"function shadowed() {}\n"
|
||||
"function other() {}\n"
|
||||
"function size() {}\n"
|
||||
"class GlobalThing {}\n");
|
||||
th_write_file(
|
||||
TH_PATH(repo, "src/Model/User.php"),
|
||||
@@ -342,10 +343,6 @@ TEST(doc_mentions_php_global_fallback) {
|
||||
ASSERT_TRUE(php_edge(db, o, "src.Model.User.shadowed"));
|
||||
ASSERT_TRUE(php_edge(db, o, "src.global.global_helper"));
|
||||
ASSERT_TRUE(php_edge(db, o, "src.Model.User.helper_here"));
|
||||
/* a function named like a method of the documented type: two readings;
|
||||
* no such function: PHP's built-ins */
|
||||
ASSERT_TRUE(php_row(db, "other()", "ambiguous"));
|
||||
ASSERT_TRUE(php_row(db, "size()", "external"));
|
||||
char props[512];
|
||||
int n = 0;
|
||||
dm_edge(db, o, "src.global.shadowed", props, sizeof(props), &n);
|
||||
@@ -369,6 +366,24 @@ TEST(doc_mentions_php_self_parent) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A bare name() in a type's doc: the type's own method first, though a
|
||||
* global function has the name too. */
|
||||
TEST(doc_mentions_php_current_class) {
|
||||
ASSERT_TRUE(php_indexed());
|
||||
const char *db = g_php_db;
|
||||
const char *o = "src.Model.User.User.other";
|
||||
ASSERT_TRUE(php_edge(db, o, "src.Model.User.User.size"));
|
||||
char props[512];
|
||||
int n = 0;
|
||||
dm_edge(db, o, "src.global.size", props, sizeof(props), &n);
|
||||
ASSERT_EQ(n, 0);
|
||||
/* the documented method itself: a self-mention, neither edge nor row */
|
||||
ASSERT_TRUE(php_row(db, "other()", ""));
|
||||
dm_edge(db, o, "src.global.other", props, sizeof(props), &n);
|
||||
ASSERT_EQ(n, 0);
|
||||
PASS();
|
||||
}
|
||||
|
||||
static void php_indexed_cleanup(void) {
|
||||
if (g_php_db[0]) {
|
||||
th_cleanup(g_php_tmp);
|
||||
@@ -463,6 +478,7 @@ SUITE(doc_mentions_php) {
|
||||
RUN_TEST(doc_mentions_php_resolution);
|
||||
RUN_TEST(doc_mentions_php_global_fallback);
|
||||
RUN_TEST(doc_mentions_php_self_parent);
|
||||
RUN_TEST(doc_mentions_php_current_class);
|
||||
php_indexed_cleanup();
|
||||
RUN_TEST(doc_mentions_php_incremental_equals_full);
|
||||
RUN_TEST(doc_mentions_php_workers_agree);
|
||||
|
||||
Reference in new issue
Block a user