Coverage

What nao can actually read.

Every row below is the behaviour of a parser in src/parser/, not a plan. The tier is a claim about which edges come out, and the edge list next to each language is the evidence for it. Where a language is weaker than you would expect from its popularity, it says so.

10dedicated parsers
6emit exact type edges
2known gaps, named below

Tier 1 of 3

Typed

Entities, call edges, and exact type edges from signatures and fields. This is the tier where impact analysis is type-accurate rather than approximate.

LanguageFilesEdges it emitsNotes
Rust .rsCallsImplementsUsesTypeThe most developed parser, and the only one with an optional exact call-edge path through rust-analyzer (NAO_LSP_EXACT=1). It is also the only language whose accuracy has been measured.
Python .py .pyiCallsInstantiatesUsesTypeWritesToType edges come from annotations. Unannotated code still yields entities and call edges, so coverage tracks how typed the codebase is.
TypeScript .ts .tsxCallsInstantiatesUsesTypeFull tree-sitter parse. On a real repository this produces roughly fifteen entities per file, with every relationship kind represented.
Java .javaCallsInstantiatesUsesTypeAlso the language the Educator rules target, so it gets good-practice hovers in the VS Code extension that the others do not.
Kotlin .kt .ktsCallsInstantiatesUsesTypeData classes are recognised as such, which is why the Data Bag smell does not fire on every one of them.
Svelte no call edges.svelteInstantiatesUsesTypeComponent-level, and much narrower than the others: it resolves which components use which, and the types in a <script lang="ts"> block.

nao's own docs count this tier as five languages rather than six: TypeScript and Svelte share one implementation, so a Svelte file gets its type edges from the TypeScript machinery reading its <script> block. Svelte is listed separately here because what it does not emit — call edges — is worth knowing before you rely on it.

Tier 2 of 3

Structural

Entities and relationships, but no type edges. impact still works — it approximates type usage through "used via members" instead of resolving it.

LanguageFilesEdges it emitsNotes
Groovy .groovy .gradleCallsInstantiatesReadsFromWritesToIncludes Spring bean wiring and embedded scripts. The named gap in the roadmap: it is the one dedicated parser still without type edges.
Impex .impexCallsReferencesWritesToSAP Hybris data-import files. Hand-rolled, because no tree-sitter grammar for the format exists — this is data flow rather than control flow.
Ansible / Kubernetes by pathContainsIncludesInterpolatesProvidesReferencesRendersFromRequiresClassified by repository layout rather than extension, since .yml belongs to nobody. The richest edge vocabulary of any parser, because deployment topology is what it models.
Elevator .elvContainsReferencesThe domain-spec language, not source code. It is parsed by the same machinery so that --drift can compare a spec against the code it claims.

Tier 3 of 3

Fallback

A line-based regex pass, not a grammar. It recognises some declarations and produces no relationships at all, so the graph has nodes but no edges.

LanguageFilesEdges it emitsNotes
JavaScript not wired up.js .mjs .cjs .jsxnoneDespite the TypeScript parser sitting right next to it, .js files are not routed to it — they fall through to the regex fallback and produce no relationships. Renaming a file to .ts is currently the whole fix.
Go no parser.gononeNo dedicated parser. tree-sitter-go is a declared dependency that nothing calls, so Go gets the same regex pass as everything unrecognised.

Anything nao does not recognise at all lands here too, alongside these: C, C++, C#, PHP, Ruby, Scala, Swift. Files are still walked, counted and drawn — you get a file tree and entity names, and nothing that depends on relationships.

The gap worth stating twice

JavaScript is not wired to the TypeScript parser.

The parser exists, handles the same grammar family, and sits in the same directory. But the dispatch table has no arm for JavaScript, so .js, .mjs, .cjs and .jsx fall through to the regex fallback and produce entities with no edges between them.

This is a routing gap rather than a missing capability, which is what makes it worth calling out: the cost of not knowing is a graph that looks sparse for reasons that have nothing to do with your code.

Until it is wired up, a TypeScript-flavoured project gets full fidelity by extension alone — a mixed repository will be analyzed unevenly, and the uneven half is the .js one.

Measured on sindresorhus/got

One repository, both languages present, one analysis run.

.ts.js
Files796
Entities1,2129
Relationships3,4580

Fifteen entities per TypeScript file; one and a half per JavaScript file, joined by nothing.

One more honest note

Only one of these has been measured.

The 93.7% recall and 98.6% precision figures quoted everywhere on this site are Rust, on nao's own source, against a rust-analyzer oracle — the only oracle wired up. Every other parser in the table above is unmeasured. Assume they are worse rather than equal.