# Generated by tools/gen_stubs.py from the package source. Do not edit. # `make stubs` regenerates; test/test_stubs.py refuses any difference. """Turn a C++ codebase into `code`-module facts. The `code` reasoning module supplies the reasoning. This supplies the facts. It reads your source and writes a DKE Python program that records what it found — functions, classes, calls, includes — which you then submit to your store with the `compile` (or `run`) tool you already have: dke-ingest-cpp ./src ./include > facts.dpy # then submit facts.dpy through your DKE client Nothing here talks to the network, and nothing here needs your key. The output is a program you can read before you run it: every fact this tool would record is a line you can see. WHY THIS IS HAND-WRITTEN. Python's ingest parses with CPython's own `ast`, so what counts as a function there is what counts as a function to Python. C++ ships no such thing, and the discipline this ecology holds to (brickmason's per-language spec-first rule) puts external parsers off-limits: no libclang, no tree-sitter, nothing vendored. So this reads C++ the way the language's own grammar describes it — scrub comments and literal bodies, tokenize, then walk the token stream tracking scope and brace depth. That has a consequence worth stating before any number below is trusted: a hand-written reader sees LESS than a compiler. It does not instantiate templates, does not expand macros, does not evaluate `#if`, and does not do overload resolution by argument TYPE. Every one of those limits costs recorded edges, never invents them — the rule throughout is that a missing edge is a known gap and a guessed edge is a wrong answer the queries cannot tell from a fact. ON CALL RESOLUTION — read this before trusting `uncalled()`. The call graph is a LOWER BOUND. An edge is recorded only when the source makes the target plain: a bare or qualified name that resolves against the enclosing scopes, `this->m()`, or a call through a variable whose type was DECLARED in the source this reader could see. A call through a function pointer, a `std::function`, a virtual dispatch, a template parameter, or a macro is not visible here and is not recorded. Virtual dispatch is the one worth naming twice: a call through a base-class reference records nothing, so an override can look uncalled while running on every request. The tool prints its resolution rate to stderr and writes it into the generated program's header. Treat `uncalled()` as a list of candidates to look at, never as a list of dead code to delete.""" import re import sys from dataclasses import dataclass from pathlib import Path, PurePosixPath from dke.ingest import core from dke.ingest.core import MODULE, Call, Dependency, Facts, Function, Ids, Import, Klass, Language, UnresolvedCall, reached_by_tests, sanitize EXTENSIONS: ... UNBOUNDED: ... def scrub(src: str, comments: list[tuple[int, str]] | None=None) -> tuple[str, list[tuple[int, str, str]]]: """(code with comments/literals blanked, [(line, directive, rest)]). `comments` is an optional SINK: when given, each comment is appended as (line, body-without-delimiters). CODE-MODULE-RATIONALE-FACTS needs the comments this function exists to throw away, and collecting them here rather than in a second pass is what keeps one definition of where a comment starts — a naive `//` scan would find one inside a string literal, which this walk already gets right.""" ... def tokenize(code: str) -> tuple[list[str], list[int]]: """(token texts, 1-based line of each token). `<` and `>` stay single characters even when adjacent: `>>` closing two template argument lists and `>>` shifting are the same two bytes, and nothing downstream needs to tell them apart — only `template <…>` counts angle depth, and a shift cannot appear there.""" ... KEYWORDS_NOT_CALLS: ... TRAILING_QUALIFIERS: ... STORAGE_WORDS: ... BUILTIN_TYPES: ... def is_ident(t: str) -> bool: ... def find_matching(toks: list[str], i: int, open_t: str, close_t: str) -> int: """Index of the token closing the group that opens at `i`, or len(toks).""" ... def arity_range(toks: list[str], lp: int, rp: int) -> tuple[int, int]: """(min, max) argument counts a parameter list `( lp … rp )` accepts. Counted lexically: top-level commas, minus the ones whose parameter has a default. This is the whole of what a reader without a type system can do about overloads, and it is enough for the common case — two overloads of one name almost always take different numbers of arguments.""" ... def call_arity(toks: list[str], lp: int, rp: int) -> int: """How many arguments a call site passes, counted the same lexical way.""" ... @dataclass class Definition: """One function DEFINITION, and the arities it accepts. Kept beside the `Function` fact rather than on it because a qualified name is not unique in C++: overloads share one. `Function.qual` is what becomes a store subject, so it has to be made unique before anything is emitted, and this record is what the disambiguation and the resolver both read.""" fn: Function base_qual: str pmin: int pmax: int module: str line: int class Pending: """A call site, kept until every definition in the ingest is known. Resolution cannot happen during the first pass for the same reason it cannot in the Python reader: a call into a file read later must still resolve, so no edge may be decided before every definition exists.""" __slots__: ... def __init__(self, caller: Function, segs: list[str], seps: list[str], argc: int, line: int, scopes: list[str], class_qual: str | None, locals_: dict[str, str], path: str) -> None: ... class FileScanner: """One translation unit or header. Walks tokens; records what it sees.""" def __init__(self, module: str, path: str, facts: Facts, defs: list[Definition], members: dict[str, dict[str, str]]) -> None: ... def scope_qual(self) -> str: ... def enclosing_class(self) -> str | None: ... def namespace_prefixes(self) -> list[str]: """Enclosing scopes as resolution prefixes, innermost first, then ``.""" ... def scan(self, code: str, directives: list[tuple[int, str, str]]) -> None: ... def disambiguate(defs: list[Definition]) -> None: """Give every definition its own subject, and say how they differ. C++ overloads share a qualified name. `Function.qual` becomes a store subject, so leaving them equal would let one function's facts silently overwrite another's — the exact failure `Ids` exists to prevent, arriving one layer earlier where `Ids` cannot see it. An overload set is therefore keyed by parameter count, which is both the thing that tells them apart at a call site and something a reader of the subject can interpret. Two overloads with the SAME count (a copy and a move constructor, say) take a further index — deterministic by file and line, and calls into such a set resolve to nothing, because argument count cannot choose between them and guessing is the one thing this reader does not do.""" ... def resolve_all(scanners: list[FileScanner], facts: Facts, defs: list[Definition]) -> None: ... def is_test_file(path: str) -> bool: """A file a C++ test build would compile as a test, by name or directory.""" ... def is_test_func(name: str) -> bool: """A test callable, by the conventions C++ test code actually uses. Narrow on purpose. C++ has no collection protocol to read — no `test_*` rule a runner enforces — so anything looser starts inferring, and a wrong test edge is worse than a missing one for the same reason a wrong call edge is.""" ... def read_dependencies(roots: list[Path], facts: Facts) -> None: """What the project DECLARED it builds against. Offline, always. C++ has no single manifest, so this reads the one that is a file in the tree: CMake. `find_package` and `pkg_check_modules` name a dependency and usually no exact version, so they are recorded unpinned — a version RANGE is not a version, and writing one would be a guess the store cannot tell from a fact. `FetchContent_Declare` carries a `GIT_TAG`, which IS exact, so it is recorded pinned. A tree built by hand — a Makefile, a shell script — declares nothing this can read, and then nothing is recorded. That is the honest answer, and it is why `unused_dependencies()` over such a tree is empty rather than wrong.""" ... def module_name_for(path: Path, root: Path) -> str: """The file, POSIX-relative to the tree it was found in. A C++ file IS the unit an include names, so the path is what makes `code.Import.imported` join with `code.Function.module` and `importers_of("src/wire.cpp")` answer.""" ... def collect(roots: list[Path], excludes: list[str]) -> Facts: ... LANG: ... def emit(facts: Facts, ids: Ids, *, stamp: int | None=None, load_key: str | None=None) -> str: """Render C++ facts as a DKE Python program.""" ... def main(argv: list[str] | None=None) -> int: ...