Bridges the OTP callback boundary: a framework call through a variable module — cowboy's Handler:init / Middleware:execute folds, a plugin manager's Mod:callback(...) — now links to the repo's implementers of the behaviour declaring that callback, so codegraph_explore connects flows end-to-end across behaviour dispatch instead of stopping at it. Precision gates: the callback arity must match the site, exactly one in-repo behaviour may declare that (name, arity) — a collision bails (cowboy's init/2 is declared by five handler-flavored behaviours and correctly stays silent) — the implementer must export the callback, and above the fan-out cap the site is skipped entirely (ejabberd's gen_mod with ~230 implementers stays a visibly dynamic boundary). Behaviour discovery scans -callback declarations in every module so implementer-less behaviours still gate ambiguity. Edges carry provenance:'heuristic' with synthesizedBy:'erlang-behaviour' and the wiring site, rendered as dynamic dispatch in explore. Validated per the dispatch-family playbook: cowboy 38 edges (middleware chain, stream-handler folds, sub-protocol upgrade), ejabberd 598, emqx 843; 36/36 sampled edges precise (target declares the via-behaviour and exports the callback); node counts unchanged; ~1.4s added on emqx's 2,273 files; zero-control clean. The cowboy request flow connects in one explore call. Includes an Erlang comment stripper (%-comments, string/atom/$-char aware) for the dispatch-site scans. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6511722250
commit
2217a35943
@@ -35,7 +35,8 @@ export type CommentLang =
|
||||
| 'go'
|
||||
| 'rust'
|
||||
| 'c'
|
||||
| 'cpp';
|
||||
| 'cpp'
|
||||
| 'erlang';
|
||||
|
||||
export function stripCommentsForRegex(content: string, lang: CommentLang): string {
|
||||
switch (lang) {
|
||||
@@ -45,6 +46,8 @@ export function stripCommentsForRegex(content: string, lang: CommentLang): strin
|
||||
return stripRuby(content);
|
||||
case 'rust':
|
||||
return stripRust(content);
|
||||
case 'erlang':
|
||||
return stripErlang(content);
|
||||
case 'php':
|
||||
return stripPhp(content);
|
||||
case 'go':
|
||||
@@ -471,3 +474,55 @@ function stripRust(src: string): string {
|
||||
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
// ---------- Erlang ----------
|
||||
|
||||
/**
|
||||
* Erlang: `%` starts a line comment unless it sits inside a `"string"`, a
|
||||
* `'quoted atom'`, or is the character literal `$%`. Strings and quoted atoms
|
||||
* are left intact (a behaviour callback name can be a quoted atom); only the
|
||||
* comment text is blanked.
|
||||
*/
|
||||
function stripErlang(src: string): string {
|
||||
const out = src.split('');
|
||||
let i = 0;
|
||||
const n = src.length;
|
||||
|
||||
while (i < n) {
|
||||
const c = src[i];
|
||||
|
||||
if (c === '"' || c === "'") {
|
||||
const quote = c;
|
||||
i++;
|
||||
while (i < n && src[i] !== quote) {
|
||||
if (src[i] === '\\' && i + 1 < n) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i < n) i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Character literal: `$x`, `$\n`, `$%` — the next char (or escape) is data.
|
||||
if (c === '$') {
|
||||
i++;
|
||||
if (i < n && src[i] === '\\') i++;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === '%') {
|
||||
let end = i;
|
||||
while (end < n && src[end] !== '\n') end++;
|
||||
blankRange(out, i, end, src);
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user