Repository navigation
Releases: cyberchronos00/code-graph
Releases · cyberchronos00/code-graph
Release list
v0.23.12
Added
- Kotlin exact mode maps scip-java property terms (
Cart#label.) and the syntheticgetLabel()/setItems()/
isReady()at the property's spelling: a stored property isREADS_PROP/WRITES_PROP, a custom accessor is
CALLSwithproperty: read,writeorread_write(from=,+=,++). Covered files drop the heuristic
accessor calls. A declaredfun getX()stays a call where the source spellsgetX(#105). - Kotlin: an interface or abstract
val/varwith no accessor becomes a property node (property: abstract) when an
override has one, withIMPLEMENTED_BYfrom an interface property andOVERRIDDEN_BYfrom an abstract or open one
(#105). - Java
getX/setX/isXon a Kotlin property uses the same property nodes in heuristic and exact mode (#105). - Swift
@AppStorage/@SceneStoragefields that share a wrapper and key are linked withSHARES_STORAGE(#235).
Changed
- Kotlin
$nameinside a string template and a field read through an extension receiver
(fun Cart.total() = items.size) recordREADS_PROP(#235). - A second branch on the same line keeps its own edge: the edge key includes
branch, Kotlin / Swift construction
branches are queued in source order, and the TypeScript extractor does the same forRENDERS(#235).
v0.23.11
Added
cg roundtrip --all(MCProundtrip(prop="")): every stored property in one pass, listing only the round trips.
About 1 s on a 5,000-field iOS app, where one call per property took minutes (#233).- Compose state in a local delegate (
var count by remember { mutableIntStateOf(0) },by rememberSaveable { },
by mutableStateOf(),by vm.state.collectAsStateWithLifecycle(),by produceState) is afield:<function>.<name>
node (property: state,hook,local) with READS_PROP / WRITES_PROP for bare uses in the function and its
lambdas (#234). - Vue Options API templates: a bare
{{ count }},:title="label"or@click="count++"reads / writes thedata()
key (template: true); av-forvariable of that name does not (#234).
Changed
cg lint async-state: a call of a project Kotlinsuspendfunction (val hits = repo.find(q)insidelaunch { })
is an await point.incomplete-cache-keycounts a Swift / Kotlin field used withoutself./this.as an input,
unless the function calls methods on it.two-writerstreats a sentinel reset (= AsyncData.Loading(),
= UiState.Idle) as a default (#233).
v0.23.10
Added
cg parity --structurepairs the missing members of a matched type inside the counterpart type: with an unmatched
member or a top-level function that makes the same paired calls (duplicatePlants↔copySelected), or with a
case .x:arm of a Swiftreceive(_:)/perform(_:)switch (handleHelpCenterClick↔case .helpCenterTapped,
casein JSON). On Bitwarden: 19 more iOS → Android and 62 more Android → iOS pairs (#227).
Changed
- A Swift
receive(_:)/perform(_:)method withcase .x:arms is no longer paired as a whole with one handler.
v0.23.9
Added
- Swift platform files:
Foo+iOS.swift,Foo.macOS.swiftand files in aniOS//macOS//watchOS//tvOS//
visionOS/folder are built for that platform and unknown on the other Apple platforms, when Xcode target membership
does not say. A guess: it never makes a dead branch, andplatforms.path_conventions: falseturns it off (#230).
Changed
- Swift and Kotlin receivers are typed through call chains (
shop.engine().run(),makeShop().engine()), locals
initialised from one (let e = shop.engine()),Selfreturn types (Sub.make()on aclass func make() -> Self) and
generic substitution (Garage<Engine>(…).get()withfunc get() -> T). Kotlin expression bodies (= Engine()) count
as the return type. Fewer name-bound and candidate edges (#229, part of #109). #availablebranches are column-precise:if #available(…) { a() } else { b() }on one line and} else { b() }
on the closing line no longer markbas guarded; conditions after the check (, x.isOn) are (#230, part of #225).
Fixed
v0.23.8
Added
cg parity --structurepairs types still missing by the fields they declare (confidencefields). Stored properties count under their wire names: SwiftCodingKeysraw values, or Kotlin@SerialName/@SerializedName/@Json(name =). So a SwiftCodablemodel meets its renamed Kotlin@Serializableport. Enum cases are compared with enum cases. On Bitwarden this adds 52 and 55 inferred pairs, mostly request / response models and their…Jsoncounterparts. See parity.md (#226, #93).- Image and raw resources (
Image("x"),UIImage(named:),Asset.Images.x,Image(.x),R.drawable.x,Res.drawable.x) are a structure feature (res:) (#226). cg parity --both-sideslists the matched pairs and what only the target has (== ONLY IN TARGET,--jsontarget_only): unmatched target types and top-level symbols, plus unmatched members of matched target types (#226).
Changed
cg parity --structure: a pair whose shared uses are all generic (found in more than three files on a side, such asl10n:cancelorl10n:yes) now needs two shared name words. Numbers are in validation-log.md (#226).
v0.23.7
Added
- Dead branches: code under nested conditional compilation whose combined condition holds on no known target (
#elseif os(macOS)inside#if os(iOS),#[cfg(windows)]inside#[cfg(unix)],#ifdef _WIN32inside#ifdef __linux__) getsattrs.dead_branch, and its calls, reads and writes to other code are dropped, soreaders/writers/impactno longer list it.cg platforms divergence --kind dead_branch(and the MCPplatform_divergencetool) lists each region with its lines, the conditions that contradict and the references left out. Conditions that also cover other systems (Cygwin, MinGW, BSDs) and directory-name guesses never count. See platforms.md (#222). - C / C++: per-platform CMake source lists (
if(WIN32),if(APPLE),if(CMAKE_SYSTEM_NAME STREQUAL "Linux"),elseif/else,list(APPEND ...),target_sources) decide which targets build a file, replacing the directory-name guess. Read without running CMake;platforms.cmake_membership: falseturns it off (#111). platforms.compile_commandsin.cg.yaml: onecompile_commands.jsonper target build gives exact C / C++ membership, winning over the CMake lists. Targets without a database stay unknown (#111).- Tauri events:
emit/emit_to/listenin Rust andemit/listen/oncein JS / TS aretauri-eventbridge endpoints linked across the process boundary, withno_receiver/no_senderchecks;tauri://built-ins and listeners with computed names are exempt. See bridges.md. - Svelte components (
.svelte) are indexed through the TypeScript extractor like Vue:<script>blocks pluson*={handler}attributes, with references on the component node. platforms.processesin.cg.yamldeclares Electron / Tauri process roles by path glob (main,preload,renderer,webview,core,worker), overriding the inferred role.
Changed
- A Linux-named file (
*_linux.c,linux/) without a build list is unknown on Android instead of absent there. __PASE__and__illumos__count as other systems in C conditions..svelteis no longer listed as unsupported bycg completeness.
Fixed
.cg.yamlvalidation rejectedplatforms.xcode_membership.
v0.23.6
Added
- Default arguments in TypeScript / JavaScript, Kotlin, PHP and Python, as already done for Swift: a call inside a parameter's default value is an edge from the declaring function with
attrs.default_arg, whichimpactlists asvia default argumentwithout walking it. In TypeScript / JavaScript, Kotlin and PHP (8.1newin initializers), where the default runs on every call, a resolved call that leaves the argument out (or passesundefinedin TS) also gets an edge from the caller to the default's callee withattrs.omits_defaultandattrs.default_of, soimpacton that callee lists the callers that run it. Named and trailing-lambda arguments count; a call with a spread argument gets none. Python evaluates defaults once atdeftime, so only the declaration edge is marked. See schema.md (#212). docs/validation-log.mdrecords thecg affectedhand-check of 20 (file → test) pairs: immichserver/20/20 after the TypeScript fix below, flask 9/20 (the misses are filed as #221), and the Swift narrowing numbers (#199, #218).
Changed
- Swift heuristic mode narrows selector candidates: a local assigned from a project function or static factory with a declared project return type (
let b = makeBridge(),let s = Store.shared(),-> Self) gets that type, and a selector with only one visible declaring type binds by name instead of as a candidate.private/fileprivatemembers, and members of a file-private type, count only in their own file; an overload set split across files or across access levels stays visible. Candidate edges drop on most corpora (Kingfisher 206 → 197, SwiftUIX 454 → 441) with exact-mode precision and recall on Alamofire unchanged; numbers in validation-log.md (#218).
Fixed
- TypeScript: an instance method no longer gets
OVERRIDDEN_BY(orIMPLEMENTED_BY) from a base class'sstaticmember of the same name, so a base service'sstatic create(...)factory is not an override target of every subclass'screate. Found by thecg affectedhand-check (#199).
v0.23.5
Changed
- Laravel API routes are stored and shown at their served path: a route in
routes/api.php(orroutes/api/) served at/api/v1/ordersisroute:POST /api/v1/ordersincg routes, route ids,route:specs and the viz, instead ofPOST /v1/orders. The prefix is read frombootstrap/app.phpwithRouting(api:, apiPrefix:), from route files mounted in itsthen:closure (Route::prefix('webhooks')->group(base_path('routes/hooks.php'))) and from a Laravel ≤ 10RouteServiceProvidergroup;apiwhen nothing configures it.web.phpstays unprefixed. Routes keepattrs.declared_uriandattrs.prefix_from. Aroute:spec with the declared path still resolves, withnote: declared path; served as POST /api/v1/orders.cg linkandcg testsmatch on the served path; the hard-coded/apivariant is used only for the default prefix and for graphs built by older versions. This affects saved plans, baselines and scripts that name Laravel API routes by id: re-index, and update ids that should show the served path (#191). routes --writesdrops writes a route reaches only through overrides its receivers cannot dispatch to, with the same receiver narrowing asdownstream(#120): its direct tables now agree withdownstreamof the route. A write reached only through an override hop whose receiver type is unknown is still listed (#149).
Added
- Laravel: routes registered in loops over literal data are unrolled:
foreachover a literal list orkey => valuearray (or a$var = [...]assigned before it, or a class constant),collect([...])->each/map,array_map/array_walkand an enum'scases(). The loop variables are substituted into the path, name, middleware,->defaults(),->where*()and the action, and each route carriesattrs.unrolled_from(file,line,binding);cg routesmarks it(unrolled: $genre = 'fiction'). A loop over data cg can't evaluate, or with more than 64 combinations, stays alaravel_loop_routesblind spot with the reason (iterable not literal), and unrolled loops are no longer counted there (#184). - Laravel routes keep
->defaults()asattrs.defaultsand->where()/whereNumber()/whereUuid()/whereIn()/ ... asattrs.where(#184). - Nuxt Nitro: object-form handlers (
defineEventHandler({ onRequest: [...], handler })) route to thehandlerfunction, withonRequest/onBeforeResponsehooks asUSES_MIDDLEWAREtargets;nitro.handlers/serverHandlersentries innuxt.configbecome routes (attrs.registered_in);routeRuleswithproxymark the routes they cover (attrs.proxy) or add a proxy route;server/directories of local layers inextendsare scanned; and aserver/middlewarehandler that returns early outside a path prefix applies only to routes under it (heuristic) (#217).
Fixed
downstreamandimpactno longer follow every override after a$this->m()orparent::m()call:$this->keeps the receiver the path entered the method with (so a queued job's$this->execute()reaches its own class's override, not every sibling's), andparent::m()runs exactly the parent method with no override hop (#148).
v0.23.4
On PyPI as cg-code-graph (pipx install cg-code-graph).
Changed
readers/writersof a Swift stored property show each site'splatforms=when the property has per-platform declarations, andfield:<Type>.<name>@<line>names one of them (#219).
Fixed
- Swift: a stored property declared again in another
#ifbranch (inside one type, or in a per-branch variant of the type) is its ownfield:<Type>.<name>@<line>node instead of being dropped, andREADS_PROP/WRITES_PROPare narrowed by platform likeCALLS(#201): a read made under#if os(macOS)links only the macOS declaration, and code built everywhere links every declaration (#219).
Full changelog: v0.23.3...v0.23.4
v0.23.3
On PyPI as cg-code-graph (pipx install cg-code-graph).
Added
- Swift: a call that leaves out a defaulted argument gets its own
CALLSedge to what the default calls, at the call site, withattrs.omits_default(the parameter label) andattrs.default_of.impacton the default's callee lists the callers that omit the argument (through an omitted default argument) and not those that pass it;callerslabels them(runs the default of gamut: in render)and the declaration edge(parameter default gamut:)(#212, Swift part).
Changed
impactandcallerscount selector-only candidates (receiver type unknown, #83) apart from resolved callers:transitive callers: 3 (+1 candidate),direct callers: 2 (2 sites); candidate callers: 1 (1 site, ...), andcallers_resolved/callers_candidatein the JSON. Candidates show their fan-out ((candidate, 1 of 3 methods named refreshStatus),candidate_ofin JSON) and are left out ofcallers by module. A caller without a module (a free function) is grouped as<file> (top level)instead of?(#204).- MCP
nodeprints neighbours as<id> :<line>, so a per-platform variant readsrefreshStatus@24 :12instead ofrefreshStatus@24@12(#201). - tree-sitter-swift 0.7.4 is supported (pin lifted to
<0.8):!Type.method()parses with the operator around the callee and is unwrapped; members after an unknown attribute macro parse without recovery. Every Swift fixture gives the same graph on 0.7.3 and 0.7.4; corpus differences are listed indocs/validation-log.md(#199, part).
Fixed
- Swift
#if/#elsevariants: a reference made where only some targets build (a macOS-only caller or call) links only the per-platform definitions built on one of those targets, so a macOS-only call no longer reaches the#elsestub and the stub no longer looks reached by macOS-only code. A reference whose definition is not built anywhere the site is moves to the sibling definition that is, and a#elsetype with noinitof its own uses its implicit initializer instead of being bound to a sibling variant'sinit, soplatform_divergencereports no false missing callee. References from code built everywhere still link every definition.callerscounts a site linked to several variants once (#201). - Swift exact mode keeps the
default_argmark on a parameter default's call, soimpactdoes not walk the declaring function as an unconditional caller (#208).
Full changelog: v0.23.2...v0.23.3