PHP properties, class constants and enum cases have no graph node, so their
doc blocks were never read: cakephp has 36 @see references there, symfony
21. The PHP leg now uses the driver's loose_docs hook: a /** */ block right
above a property, constant or case is harvested with the type around it as
the source (as the field test's prototype attached it), and resolved like
any other reference written in that type (self:: is the type).
The driver marks as the file's only what a hook pushes with the file
stand-in (the C leg's prototype docs); a hook may push with a definition of
the file instead.
Proof (ship build, Apple M5 Pro / 48 GB, base 80e000a9):
- koel 04ed8beaec34: all 5 of the prototype's pairs bound alike (the fifth
sits in a class constant's doc).
- cakephp a75828db: 373 edge mentions + 44 rows = every one of the 417
non-URL references (was 381 of 417).
- symfony 9917e115: 459 + 219 = 678 of 681 (the other 3 sit in blocks before
code the census does not classify).
Tests: doc_mentions_php resolves a property's doc block to an edge from its
class; red with the hook's walk reverted, and red when the driver marks
every token of the file pass as the file's.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
A PHP doc block names code with `@see Ref` at the start of a tag line (a
description may follow) and with inline `{@see Ref}`, both family `see`.
doclink_php.c harvests them (code spans and fenced blocks hold none; @link,
@uses and the other tags are not harvested; a URL is the core's href family)
and writes each file's scope: namespace blocks, use imports, types with
their supertypes already qualified in their own file, members, functions.
The persisted scope drops lines and imports: other files read names only.
Declarations inside a top-level conditional count as top level
(`if (!function_exists('f')) { function f() {} }`), never a function body.
doc_links_php.c resolves a reference as a FQSEN with PHP's own name rules:
- a leading backslash is taken as written; otherwise the first segment is
replaced when it is the alias of a class `use` of the namespace block the
reference is written in, else the block's namespace is put in front
(phpDocumentor's resolver);
- 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/static/parent name the documented
type and its 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; the reference does not say which);
- members are looked up on the type, its traits, its superclass and its
interfaces (depth first); type, function and method names compare without
case, properties and constants with it;
- properties, class constants and enum cases have no graph node: graph_gap;
so has a type or function the graph folds into another one of the same
name in its file (two namespace blocks), which the scope flags;
- a name the repository does not declare is external outside the
repository's namespace roots (built-ins, Composer dependencies), else
missing. Residual: a function a dependency declares in the current
namespace is invisible, so the fallback would bind a global function of
that name.
There is no scope_delta hook yet: a declaration change is a full run.
The family is held (ships=false) until its held-out audit passes; the suite
ships it through the test seam.
Proof (ship build, Apple M5 Pro / 48 GB, base 196ca71cda):
- koel 04ed8beaec34 vs the field test's prototype: 4 of its 5 pairs bound
alike; the fifth sits in a class constant's doc block, which has no node.
- cakephp a75828db: 342 edge mentions + 39 unresolved rows = every one of
the 381 non-URL references in function and type doc blocks; 36 more sit
in property and constant doc blocks (no node, not harvested).
- symfony 9917e115: 452 edge mentions (434 edges) + 205 rows = all 657; 24
in property, constant and other doc blocks.
- edges only through the global fallback: 1 (cakephp `@see debug()` in
Cake\Core, the global debug() helper: correct); one more on symfony was
wrong (`@see run()` meaning the class's own method bound a global script
function) and the method-name rule above makes it ambiguous. Through
self/static/parent: 20, all correct (12 + 5 self, 3 parent).
- a random sample of 24 edges (12 per corpus) read against their source
lines: all correct.
Tests (doc_mentions_php): tokens with their lines; the scope blob, its
persisted form, namespace blocks and conditional declarations; every
resolution rule; the global fallback and self/static/parent each in a test
of its own; incremental equals full (a doc edit and a body edit repair one
file, a removed function is a full run); 60 modules give the same edges and
rows with one worker and several. Each rule red with it reverted (global
fallback, self/static, parent, trait members, the folded-name gap, use
aliases, method names without case, the bare-name method lookup, the
namespace roots, code spans, the method-name guard, conditional
declarations).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>