You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CodeGraph now indexes Nix (.nix) — flakes, NixOS and home-manager modules, overlays, and package sets join the graph: let and attrset bindings, functions (simple, destructured { pkgs, ... }, and curried), and inherit bindings all become searchable symbols, with call edges between bindings. File-level wiring follows the ways Nix actually connects files: import ./relative/path.nix (with import ./dir reaching the directory's default.nix), NixOS module imports = [ ./hardware.nix ../common ] lists, flake-style modules = [ ./configuration.nix ] lists, and the nixpkgs callPackage ./pkgs/foo { } idiom — so "what does this configuration actually pull in" and "what uses this module" are answerable on real setups. Dynamic references (import <nixpkgs>, variable paths, flake-input module references) are deliberately left unlinked rather than guessed. Thanks @TyceHerrman. (#324, #332, #648)
The NixOS module system's option wiring is bridged, so flow questions cross the module boundary instead of going dark at it: a config write like launchd.user.agents.myapp = { ... } or home.file.".gitconfig" = { ... } links to the module that declares that option (options.launchd.user.agents = mkOption { ... } — flat and nested declaration spellings both count, quoted keys like system.defaults.NSGlobalDomain."com.apple.dock" match their exact declaration), which makes "how does enabling this service produce the launchd daemon / generated config file" traceable end-to-end and "what sets this option" answerable across modules, tests included. Precision is deliberately conservative: interpolated ${...} paths, options declared in more than one module, and submodule-internal option namespaces stay unlinked rather than guessed, and every bridged hop is labeled as heuristic module-system wiring rather than shown as a plain reference.
CodeGraph now indexes ArkTS (.ets) — the language of HarmonyOS / OpenHarmony apps. Everything TypeScript gets extracted (classes, interfaces, enums, type aliases, imports/exports, call edges), plus ArkTS's own constructs: @Component / @ComponentV2 structs with their decorators (@Entry, @State, @Prop, @Link, @Local, @Param, …) captured and searchable, build() view trees linked parent→child so "which pages render this component" is answerable, chained attributes connected to the @Extend/@Styles functions they invoke, @Builder methods and functions wired into the call graph, and .onClick(this.handler)-style event bindings linked to their handler methods. Modular HarmonyOS projects resolve across module boundaries too: a bare import { CartRepository } from "data" follows the oh-package.json5file: dependency to the right module — honoring each module's declared main entry, from .ets and .ts consumers alike — while ambiguous names in multi-app monorepos deliberately stay unlinked rather than guessed, and mixed .ets/.ts codebases cross-link freely. Validated on real HarmonyOS apps including the official OpenHarmony samples monorepo. (#396, #512, #648, #890)
ArkUI's dynamic hops are bridged so flow questions cross them instead of going dark, each labeled as dynamic dispatch rather than shown as a plain call: methods that assign a reactive property (@State, @Local, …) link to the component's build() (the re-render hop — assignment-gated, so a method that merely reads state gets no edge); emitter.emit(eventId) links to the matching emitter.on/once subscriber when both sides share a statically-recoverable event key (numeric ids pair within one file only, named constants within one module, so unrelated samples in a monorepo never cross-link); and router.pushUrl({ url: 'pages/Detail' }) links to the target page's @Entry struct, with ambiguous urls left unlinked rather than guessed.
Interrupted or incomplete indexing is now visible instead of silent: a run killed mid-index (crash, out-of-memory, watchdog) leaves a marker that codegraph status reports as a truncated index, a completed run that dropped files reports itself as partial — both in the human output and in status --json — and codegraph index prints a warning with the exact counts when its result doesn't add up to what the scan discovered.
CodeGraph now indexes Terraform and OpenTofu (.tf, .tfvars, .tofu) — resources, data sources, modules, variables, outputs, providers, and every locals attribute become symbols (e.g. aws_s3_bucket.my_bucket, var.region, module.vpc, local.prefix), and uses like var.region, module.vpc.id, data.aws_caller_identity.current, or aws_s3_bucket.my.arn are wired up cross-file, so search, callers, and impact queries return real results on infrastructure repos instead of nothing. Module calls are bridged across the module boundary: a module block's inputs link to the child module's variables, module.vpc.vpc_id reaches the child's output "vpc_id" definition, and the block's local source path links to the module's files — so "what breaks if I change this module's variable" reaches every caller instead of dead-ending at the declaration (registry and git sources are deliberately left as visible boundaries rather than guessed). Cross-component wiring through the cloudposse/atmos remote-state module connects too: module.vpc.outputs.vpc_cidr in one component reaches the vpc component's own output when the component name is statically declared (a literal, or a variable with a literal default) and exactly one directory matches — anything dynamic or ambiguous stays unlinked. Aliased providers are first-class: provider "aws" { alias = "east" } gets its own symbol, and provider = aws.east on a resource (or a module's providers map) links to that configuration, found up the module tree the way Terraform actually inherits it. moved/import/removed state-migration blocks and check assertions reference the resources they name, so a refactor's paper trail is part of the graph. .tfvars assignments link to the variables they set, including var-files kept in a subdirectory. Resolution follows Terraform's real per-directory scoping, so same-named variables across modules never cross-link and "what depends on var.project_id" in a multi-module repo never mixes in unrelated modules. Thanks @Javviviii2. (#83, #310, #648)
CodeGraph now indexes CUDA (.cu, .cuh) — kernels, device/host functions, structs, and classes become symbols, and the host→kernel call edge survives the <<<grid, block>>> launch syntax, so questions like "how does this call reach the GPU kernel?" trace across the CPU/GPU boundary instead of going dark at the launch site. Real-world launch styles all connect: templated launches (my_kernel<Traits, 256><<<grid, block>>>(args)), launches through a local function pointer (auto kernel = &my_kernel<...>; ... kernel<<<grid, block>>>(args) — each branch-assigned target linked), brace-initialized launch configs (<<<dim3{1,1,1}, dim3{256,1,1}>>>), and kernels defined through a name-in-first-argument macro (flash-attention's DEFINE_FLASH_FORWARD_KERNEL(kernel_name, ...) { ... } style), which now index under their real kernel names. CUDA that lives in plain .h/.hpp headers — where much real-world device code sits, launch-template headers included — is recognized by content and indexed the same way. Validated on llm.c, flash-attention, and NVIDIA CUTLASS. (#387, #648)
C++ symbols defined inside namespace blocks now carry the namespace in their qualified name (flash::compute_attn, C++17 namespace a::b { included), and namespace-qualified calls (ns::fn(...)) resolve to their definitions — previously such calls never linked at all, which hid much of the call graph in namespace-heavy C++ codebases from callers and impact analysis.
C++ calls that spell out template arguments (fn<T, 256>(args)) now link to the function they instantiate, the same normalization templated base classes already had.
CodeGraph now indexes Solidity (.sol) — contracts, libraries, interfaces, structs, enums, modifiers, events, errors, and state variables become first-class symbols, with call edges that follow emit, revert, modifier guards (onlyOwner-style, including base-constructor chains like constructor() ERC20(...)), and library/method calls (including using directives). import directives resolve to the imported file, so cross-contract questions like "trace transferFrom through allowance and balance updates" or "how does onlyRole reach the role storage?" work out of the box on real Solidity codebases (validated on solmate, solady, and OpenZeppelin Contracts). Thanks @naiba. (#374, #648)
Erlang behaviour dispatch is now followed through the graph: a framework call through a variable module — cowboy's Handler:init/Middleware:execute folds, a plugin manager's Mod:callback(...) — links to the repo's implementations of the behaviour that declares that callback, so flow traces and impact cross the OTP callback boundary instead of stopping at it. The links are precision-gated: the callback arity must match, exactly one behaviour may own that callback shape (a collision stays unlinked rather than guessed), the implementer must actually export the callback, and the fan-out is bounded — a behaviour with hundreds of implementers stays a visibly dynamic boundary. Every bridged hop is labeled as dynamic dispatch with its wiring site, never shown as a plain static call.
CodeGraph now indexes Erlang (.erl, .hrl) — functions, with clauses and arities of the same name grouped as one symbol spanning all of them, plus records with their fields, -type/-opaque aliases, -define macros, and -spec signatures attached to every function. Cross-module mod:fn(...) calls resolve to the target module's function, fun name/arity values are captured as references (so callback registrations like lists:foreach(fun submit/1, ...) link up), -include/-include_lib connect to the header files they pull in, -behaviour declarations link a callback module to its behaviour (and only ever to a module — a same-named macro or function elsewhere in the repo is never mistaken for one), and -export lists (plus -compile(export_all)) drive each function's public/private flag. OTP's indirection idioms are followed where the target is static: spawn/apply/proc_lib/timer/rpc calls that name their target as (Module, Function, Args) arguments produce call edges, and gen_server:call/cast connects to the target module's handle_call/handle_cast — its own when targeting ?MODULE (including the -define(SERVER, ?MODULE) idiom), and the named module when a registered name follows OTP's name-the-server-after-its-module convention (gen_server:call(other_mod, ...), directly or through a -define(STORE, other_mod) macro); a registered name that matches no module stays unlinked. Macros participate in the graph too: a -define body's calls belong to the macro, each ?MACRO(...) use site links into the call chain (and bare ?CONSTANT reads are tracked as references), so a call path hidden behind a macro — set_password → ?SQL_UPSERT_T → sql_query_t — traces end-to-end and "where is this macro used" is answerable. escripts index like any module (the shebang line is understood), and OTP application resource files (.app.src, .app) join the graph: {mod, ...} links an app to its callback module and {applications, [...]} connects umbrella sibling apps — resolving only ever to modules, so an OTP app name like ssl is never mistaken for a same-named function. Truly dynamic dispatch (Mod:handle(...), message sends, var-module spawns) is deliberately left unlinked rather than guessed. codegraph_explore also understands Erlang-native symbol spelling in queries — mod:fn/3 and init/2 find the symbols they name. (#635, #648)
CodeGraph now indexes Visual Basic .NET (.vb) — classes, Modules, interfaces, structures, enums, properties, events, MustOverride abstract members, and Declare P/Invoke signatures, with Inherits/Implements hierarchy edges, call edges (resolved through VB's ambiguous call-vs-index parentheses), and New/As New instantiation links. Real-world VB styles parse cleanly: WinForms designer files, interpolated and multi-line strings, XML literals (embedded <%= %> expressions included), single-line and multi-line LINQ queries, multi-line lambdas, Handles/WithEvents event wiring, Custom Events, date literals, classic type-character identifiers (i%, name$), and non-English (Unicode) identifiers. (#648, #639, #170)
CodeGraph now indexes COBOL (.cbl, .cob, .cpy) — programs, sections and paragraphs with PERFORM/GO TO call edges, CALL cross-program calls, COPY copybook imports (standalone copybooks included), and DATA DIVISION records with 88-level condition names, in both fixed and free source format. Impact queries work on data items: every MOVE/ADD/COMPUTE/SUBTRACT write-site links back to the field it changes, so "what touches this copybook field" answers across programs. CICS flows connect too: EXEC CICS LINK/XCTL program targets, EXEC SQL INCLUDE copybooks, and pseudo-conversational RETURN TRANSID(...) hops resolve to the program owning the transaction id. (#590, #648)
CodeGraph now indexes CFML (.cfc, .cfm, .cfs) — both the classic tag-based style (<cfcomponent>/<cffunction>) and modern bare-script component { ... } syntax, including extends/implements, embedded <cfscript> blocks (at any nesting depth, including inside <cfif>/<cfloop>/<cftry>), call edges, and calls embedded in #hash# expressions inside <cfquery> SQL bodies. Files saved with a UTF-8 byte-order mark and tags with unquoted attribute values — both common in long-lived CFML codebases — are handled too. Thanks @ghedwards. (#1118)
CFML inheritance written as a component path now links to the right component. extends="coldbox.system.web.Controller" names its supertype by dotted path and extends="../base" by relative path (the FW/1 style) — both previously produced no inheritance edge at all, which on framework-style CFML apps hid most of the type hierarchy from impact and blast-radius analysis (on ColdBox's own core, over 90% of inheritance was invisible). Resolution is deliberately conservative: the target's directory layout must corroborate the declared path — so a supertype that lives in an out-of-repo library (testbox, mxunit, an installed framework) correctly stays unlinked rather than being guessed at, and an ambiguous path produces no edge rather than a wrong one. (#1152)
CFML method calls made through a local variable, typed argument, or component property now resolve to the right method — the same receiver-type inference the other object-oriented languages already had. var svc = new UserService(); svc.save(), createObject("component", "path.UserService"), a typed <cfargument> or cfscript parameter, and variables./this.-scoped fields — including the pseudoconstructor pattern (variables.svc = new UserService() in init()) and WireBox-injected properties (property name="svc" inject="UserService") — all now link the call to the declared component's method, with methods inherited from a supertype resolved through the inheritance links above. This makes callers, impact/blast-radius, and codegraph_explore flow traces follow CFML service calls instead of dropping them or guessing among same-named methods.
The Claude Code context hook now recognizes prompts that describe code in plain words — in any language — by checking the prompt's words against the symbol names actually in your project's index. Asking about "the state machine des commandes" finds OrderStateMachine with no keyword involved. Confidence decides how much gets injected: structural questions and prompts naming a real symbol still get full context up front; a plain-words match gets a short pointer to the matching symbols so the agent queries them itself; everything else stays silent, exactly as before.
Anonymous usage telemetry now counts how often the context hook injected context, offered a hint, or stayed silent — fixed counter names only; the prompt's content is never stored or sent. This makes the hook's accuracy measurable instead of guessed. The counters record what actually happened, not what was attempted: a lookup that errors or comes back empty counts as a distinct silent outcome, never as delivered context (#1143, thanks @inth3shadows).
Metal shader files (.metal) are now indexed. Metal Shading Language is close enough to C++ that vertex/fragment/kernel functions, structs, type aliases, and the calls between them all land in the graph — so shader pipelines in Apple-platform projects show up in impact analysis and flow traces instead of being silently skipped. Metal's [[buffer(0)]]-style attribute annotations are handled so they can't corrupt what gets extracted. Thanks @FluxKo for the report. (#1121)
CodeGraph now indexes legacy iBatis 2 SQL maps (<sqlMap>), not just MyBatis 3 <mapper> files. <select>/<insert>/<update>/<delete>, iBatis's <statement>/<procedure>, and <sql> fragments inside a <sqlMap> become searchable statement symbols — for both namespaced maps and the namespace-less Map.statement id style — and <include> references resolve to the fragment they pull in, so search, callers, and impact queries return results on iBatis codebases that previously produced no statement symbols at all. Thanks @ESPINS for the report and the reproduction. (#1182)
You can now force gitignored first-party source into the index with an include list in codegraph.json. The case this solves: a project tracked by a second VCS (SVN, Perforce, …) alongside Git, where some real source is committed to that VCS and deliberately listed in .gitignore so it never lands in Git — git never lists those files, so CodeGraph never indexed them, and neither includeIgnored (which only revives embedded git repositories inside a gitignored directory) nor exclude (its opposite) could help. Add a root codegraph.json with, e.g., { "include": ["Tools/", "Local/typescript/"] } and CodeGraph discovers those files directly off disk — overriding .gitignore — and indexes them on the full index, incremental sync, and file-watching, on both git and non-git projects. Patterns are gitignore-style and matched against project-root-relative paths (a directory, a recursive ** glob, or a single file). An explicit exclude still wins, and built-in skips like node_modules, dist, and .git are never re-included. This complements the existing exclude (its opposite — keep tracked files out) and includeIgnored (opt in to gitignored embedded repos).
Fixes
Indexing a large Java or Kotlin Spring monorepo is dramatically faster. The reference-resolution phase — the bulk of a first-time codegraph index — could run for the better part of an hour on a big multi-module project and now finishes in a few minutes, producing the same graph. The cause: every receiver.method() call in the codebase was repeatedly scanning the project's entire set of configuration keys looking for a match — work that only ever applies to Spring @Value / @ConfigurationProperties bindings, never to ordinary method calls. As part of the same fix, a method call whose name coincides with a configuration key (say a service.process() call alongside a service.process entry in application.yml) is no longer mislinked to that config key — it now resolves to the actual method. Thanks @bayernjava for the report. (#1180)
codegraph init at a parent repository whose .gitignore excludes its child repositories no longer silently indexes nothing and reports success. The "super-repo of gitignored child repos" layout — a top-level Git repo that .gitignores each service-*/ or packages/* child so git status stays quiet — used to index only the parent's few top-level files and print "Done" with 0 nodes, even though running codegraph init inside any child worked fine (CodeGraph respects .gitignore by default, so the excluded children were skipped). Now, when an index comes up empty, CodeGraph detects the gitignored child repositories that were skipped, names them, and — in an interactive terminal — offers to index them (writing an includeIgnored entry to codegraph.json and re-indexing on the spot); non-interactive runs print the exact codegraph.json snippet to add. Projects that legitimately keep gitignored reference clones out of a working index are never nagged: the offer only appears when the index would otherwise be empty. Thanks @small-thanks for the report. (#1156)
The MyBatis mapper reader is sturdier on real-world XML. Single-quoted attribute values (id='getById', legal XML and common in older mappers) are no longer skipped, so those statements make it into the graph. Statements and <include>s that were commented out with <!-- ... --> no longer produce phantom symbols. And two vendor-split statements — the same id with databaseId="oracle" / databaseId="mysql" — written on a single line no longer silently drop one of the pair. Thanks @ESPINS for the report, the reproductions, and the fixes. (#1182)
codegraph init and codegraph index no longer get killed by the safety watchdog at the "Resolving refs" step on large method-name-heavy codebases (big Java/enterprise monorepos were the main victims, especially on slower machines). Resolution used to come up for air only every 500 references, so a dense stretch of expensive ones could starve the watchdog long enough for it to assume the process was stuck and kill a perfectly healthy index. Resolution now checkpoints after every reference, and two of the expensive steps got much cheaper: repeated method lookups on the same type are now cached, and source files are no longer re-split line-by-line for every call being resolved — indexing such repos is several times faster as a result. Generated or minified single-line files are also skipped during receiver-type inference instead of being scanned per call. Thanks @UchihaYong and @wangmeng-95 for the reports. (#1122)
An index left incomplete by an interrupted run now heals itself on the next sync instead of silently staying wrong forever. If indexing died partway through resolving references (a crash, Ctrl-C, or the watchdog kill fixed above), the affected files still looked indexed but their caller/impact edges were missing — a too-small blast radius clustering by package or module, e.g. a Spring @Resource-injected method reporting 3 of its 10 real caller files — and because incremental syncs only re-resolve files that changed, the damage was permanent until a full re-index. Any sync (a watched file change, or a bare codegraph sync) now detects the leftover references and finishes resolving them, codegraph status warns when an index is in that state instead of passing it off as healthy, and a rare early-stop that could abandon resolution on repos whose first files reference only external libraries is fixed too. Thanks @KnifeOfLife for the report and the package-correlation observation that pinned it down. (#1187)
The shared background server no longer shuts down out from under a live editor/agent session that simply hasn't queried CodeGraph in a while. A safety timer meant to reap an abandoned server — one whose client vanished without the connection ever closing — was reaping any server that saw no requests for 30 minutes, including a perfectly live session that just wasn't asking CodeGraph anything; that silently dropped the session (and every other session sharing the same background server) to a slower in-process mode for the rest of its life. On one machine over a day it fired 20 times on live sessions and caught zero real phantoms. The timer now checks whether the connected clients are actually still alive and only reaps when none of them are, so a quiet-but-live session keeps its shared server while a genuinely abandoned one is still cleaned up. (#1200)
CodeGraph's background server no longer leaves a lingering Node process behind when your editor or agent kills its launcher during startup. If the app that started CodeGraph (Codex, Claude Code, or another MCP client) was killed within the server's first fraction of a second — a config probe, a cancelled request, a startup timeout — while keeping the connection's pipes open, the server could be handed off to the system before its orphan-detection watchdog had captured a reference point, leaving it running (idle, ~30 MB) until the launching app itself exited; over a long day of repeated launches these accumulated. The server now records its parent at the earliest possible moment, and the npm and standalone launchers pass the real host's process id down to it so it can watch the host directly instead of only the launcher that may already be gone. As a final backstop, a server that never receives a single request after starting now shuts itself down instead of waiting for the host to exit — tunable with CODEGRAPH_STARTUP_HANDSHAKE_TIMEOUT_MS (0 disables). Thanks @ruslan33321 for the report. (#1185)
The automatic context hook for Claude Code now fires for structural questions asked in nearly thirty languages — French, Spanish, Portuguese, German, Italian, Dutch, Polish, Czech, Romanian, Hungarian, Greek, Swedish, Danish, Norwegian, Finnish, Russian, Ukrainian, Turkish, Indonesian, Vietnamese, Thai, Hindi, Arabic, Farsi, Hebrew, Japanese, Korean, and both simplified and traditional Chinese — instead of just English and simplified Chinese. Previously a natural question like "comment marche la state machine des commandes ?" injected nothing unless it happened to contain a code-shaped symbol name, making the hook look broken for non-English teams. English questions phrased with derived word forms ("explain the architecture…", "what are the dependencies…") now fire too, and prompts in any other language still fire when they name a symbol from the index. Thanks @anthonyle-roy-lgtm for the report. (#1126)
Lua and Luau method calls with capitalized names (obj:Method() — the standard Roblox convention) now link to the right method. Because Lua's method-call syntax looks identical to a Luau type annotation, a capitalized call like lg:Log() was misread as declaring the variable's type, so whenever two or more classes shared a method name (Init, Update, Destroy, …) the call was silently dropped from callers, impact/blast-radius, and flow traces. Lowercase method names were unaffected. Thanks @inth3shadows for the precise root-cause analysis and repro. (#1124)
Removed dead code left behind by the discontinued managed-reasoning feature. Its codegraph login flow was unplugged before ever shipping in a release, but the unused module still shipped inside the platform bundles, and a security review flagged its Windows browser-open step (it routed the login URL through cmd, which would have been unsafe had the flow ever been wired back up). The leftover module and its tests are now fully deleted. Thanks @inth3shadows for the report. (#1114)
The Claude Code context hook no longer treats ordinary English words that merely start with "call", "trace", "affect", or "connect" — callus, calligraphy, Connecticut, connective, affectionate, Tracey — as structural questions, which used to inject full CodeGraph context into prompts that had nothing to do with code structure. Genuinely structural forms (calls, callers, callbacks, call site, traced, tracing, affected, connections, connectivity, …) still fire exactly as before. Thanks @inth3shadows for the report. (#1138)
A stuck git command can no longer hang CodeGraph indefinitely. The git checks behind worktree detection and git-hook setup, and the installer's optional npm install -g step, now time out and fail gracefully instead of blocking forever — this matters most for the background MCP server, where an unbounded git hang (network filesystems, a wedged fsmonitor) could previously freeze it long enough for the safety watchdog to kill it. Thanks @inth3shadows for the report. (#1139)
The context hook's new plain-words matching works immediately on projects indexed by an older CodeGraph version. The word lookup it relies on is built at index time, so a project indexed before the upgrade had an empty one, and the hook would silently find nothing until something else happened to refresh the index; the hook now fills it in on first use (a one-time step — normally the background MCP server's startup catch-up gets there first). Thanks @inth3shadows for the report. (#1142)
Several accuracy fixes to the plain-words matching: a renamed symbol (for example a NestJS route after its module prefix is applied) stays findable under its new name (#1141); a word that only appears in your code as an import statement's package name is no longer presented as a matched symbol (#1144); plural words no longer generate garbled lookup keys ("services" no longer also looks up "servic") (#1145); and a name matching both the singular and plural of one word can no longer squeeze out a genuine two-word match (#1146). Thanks @inth3shadows for the reports.
Heavily-reflected Unreal Engine C++ classes are no longer dropped from the index. Reflection markup that decorates members — UPROPERTY(...), UFUNCTION(...), UCLASS(...), GENERATED_BODY(), UE_DEPRECATED_*(...), DECLARE_DELEGATE_*(...) — are no-semicolon macro calls that tree-sitter doesn't recognize, so each drops into error recovery; in a big class the errors pile up until the whole class_specifier collapses and the class, its base clause, and its members vanish (UCharacterMovementComponent, with ~240 such macros, disappeared entirely, breaking every subclass/type-hierarchy and blast-radius query that went through it). These line-leading annotation macros are now blanked (offset-preserving) before parsing so the class survives. Thanks @luoyxy for the report and root-cause analysis. (#1093 follow-up)
Unreal Engine members and methods prefixed by an export/visibility macro are no longer lost. The *_API macro doesn't only sit on the class header — it prefixes almost every exported member of a large UE class (ENGINE_API virtual void Tick(...), static ENGINE_API void AddReferencedObjects(...)); the parser read the macro as an extra type token and each such declaration fell into error recovery, so on headers like Actor.h and World.h hundreds of return types piled up as orphan errors and could still tip the class into collapse. Member/method-level *_API / *_EXPORT / *_ABI macros (Unreal, Qt/Boost, LLVM) are now blanked before parsing, mirroring the existing class-header recovery. (#1093 follow-up)
Unreal Engine annotation macros that appear mid-line — an enum value's UMETA(DisplayName=...), a parameter's UPARAM(ref), or a deprecation tag wedged into a using alias (using FOnNetTick UE_DEPRECATED(5.5, "...") = ...;, which alone collapsed UWorld in World.h) — are now stripped too. These sit in positions the line-leading recovery structurally can't reach, and a single one could take down the surrounding enum or class. They are matched by an Unreal-only name list (UMETA, UPARAM, UE_DEPRECATED*) so no standard-C++ or other-library code is affected. Together these three fixes recover the main class of every large Unreal Engine header tested (Actor, ActorComponent, SkeletalMeshComponent, World, LightComponent, CharacterMovementComponent). (#1093 follow-up)
A C++ header whose only C++ signal is an export-macro-annotated class is no longer misdetected as C — which had silently dropped the class. A .h file defaults to C and is only reclassified as C++ when it shows a C++-specific construct, but that check couldn't see through an export/visibility macro: class ENGINE_API UFoo : public UObject didn't match the macro-blind class Name : pattern, so a lean Unreal-Engine-style header carrying just GENERATED_BODY() and no explicit public: / virtual / namespace / template was parsed as C. The C extractor emits no class nodes, so the class — and its inheritance link — vanished from the graph, quietly undoing the macro-class recovery added in #1061. The C++ detection heuristic now recognizes an export-macro-annotated class/struct declaration (the same shape #1061 blanks before parsing), matching how class Foo : Bar was already detected; the two-token <keyword> <MACRO> <Name> before a [:{] never occurs in valid C, so genuine C headers are unaffected. Thanks @luoyxy for the report and root-cause analysis. (#1133)