cg-code-graph 0.23.9__tar.gz → 0.23.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cg_code_graph-0.23.9/cg_code_graph.egg-info → cg_code_graph-0.23.11}/PKG-INFO +1 -1
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/__init__.py +1 -1
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/cli.py +10 -3
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/lint_async.py +47 -10
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_server.py +5 -2
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/parity_structure.py +136 -15
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/plugin.py +39 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/extract.mjs +17 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/roundtrip.py +71 -7
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11/cg_code_graph.egg-info}/PKG-INFO +1 -1
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/SOURCES.txt +2 -0
- cg_code_graph-0.23.11/tests/test_kotlin_local_state.py +77 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_lint_async.py +21 -4
- cg_code_graph-0.23.11/tests/test_parity_members.py +67 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_roundtrip.py +12 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_fields_js_vue.py +15 -4
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/LICENSE +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/MANIFEST.in +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/README.md +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/__main__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/affected.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/agent_rules.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/agents.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/aitools.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/android_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/apple_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/apps.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/blindspots.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/bridges.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/brokers.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/cmake_sources.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/concepts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/config.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/cache.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/detect.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/env.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/extractors.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/fsutil.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/generated.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/model.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/paths.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/redact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/store.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/syntax_errors.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/coverage.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/dart_isolates.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/doctor.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/explore.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/external.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/graphql.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/guard_facts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/hooks.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/indexer.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/insecure_transport.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/install_rules.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/jobs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/link.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/local_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/local_sockets.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_graphs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_install.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/parity.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/payload.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plans.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/platform_scan.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/platforms.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/content.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/syntax.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/bridges.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/bin/extract.dart +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/pubspec.lock +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/pubspec.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/http.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/models.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/program.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/extras.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/models.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/schemas.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/shapes.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/urls.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/express/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/express/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/flutter/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/flutter/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/interop.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/spring.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/reparse.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/broadcast.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/massassign.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/tests.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/gates.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/runner.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/scipread.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/ts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nest/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nest/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nextjs/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nextjs/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nuxt/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nuxt/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/external.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/composer.json +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/composer.lock +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/extract.php +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/gating.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/http_client.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/inline_guards.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/strings.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/aitools.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/external.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/refs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/roots.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/socketio.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/subproc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/tests.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/cargo.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/syntax.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/importer.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/scip.proto +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/scip_pb2.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/stubs/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/stubs/plugins.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/baseurl.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/indexstore.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/packages.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/baseurl.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/fw.mjs +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/package-lock.json +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/package.json +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/rr.mjs +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/nav.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/plugin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/react_router.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/common.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/data.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/c_cpp.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/common.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/dart.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/django-ninja.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/django.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/djangorestframework.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/express.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/java.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/kotlin.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/laravel.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nest.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nextjs.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nuxt.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/php.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/python.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/react-router.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/rust.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/spring.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/swift.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/typescript.yaml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/process_runs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/builtin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/matchers.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/view.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/query.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_events.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_native.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_ws.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/routes.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/rpc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/sdk_systems.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/sockets.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/ssrf_input.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/starters.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/surface.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/tests_index.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/udp_apps.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/update_check.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/__init__.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/graph.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/server.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/app.css +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/app.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/index.html +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/layered.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/VERSIONS.txt +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cose-base.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cytoscape-fcose.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cytoscape.min.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/layout-base.js +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/package-lock.json +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/package.json +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/shoot.mjs +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/watch.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/webhook_subs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/webhooks.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/xcode.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/dependency_links.txt +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/entry_points.txt +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/requires.txt +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/top_level.txt +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/pyproject.toml +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/setup.cfg +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_affected.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_agents_install.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_agents_md.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ai_tools.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_android_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_apple_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro_content.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro_server.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_bridges.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_bridges_pigeon_monorepo.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_brokers.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_clean.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_invalidation.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_migration.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_channels_tests.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cli_output.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cmake_sources.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_completeness.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_config_extras.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_coverage.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_coverage_ts_hint.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dart_isolates.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_db_connections.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dead_wrappers.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dispatch_interfaces.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dispatch_part3.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_django_flutter.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_docs_languages.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_docs_links.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_echo_ref.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_echo_wrapper.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_elysia.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_enum_constants.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_env.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_explore.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_external.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_fp_batch.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_gating.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_generated.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_graphql.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_hooks.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_impact_overrides.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_index_isolation.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_inherited_overrides.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_inline_guards.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_install_rules.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_192.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_201.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_202.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_203.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_204.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_205.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_206.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_207.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_208.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_209.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_210.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_211.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_211_used_at.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_212.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_212_langs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_218.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_219.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_222.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_java.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_java_exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_jobs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_js_refs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_js_runtime.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_binding.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_mixed.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_nav_consts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_props.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_determinism.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_receiver_narrowing.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_resources.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_route_paths.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_link_origin.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_listing_files.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_local_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_local_sockets.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mass_assignment.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_index.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_install.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_instructions.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_multi_graph.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_cfamily.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_concurrency.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_rust.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_i18n.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_import_dirs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_nitro.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_rootsrc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_side_dirs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_packaging.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity_fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity_structure.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_php_http.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_php_props.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_plain_js.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_plans.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platform_gaps.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platform_type_variants.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platforms.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platforms_apple.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_presets_config.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_process_ipc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_process_runs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_protocols.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_app_tests_module.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_collection_chains.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_compat.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_entry_refs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_infer_cycles.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_roots.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_subprocess_tests.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_tests.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_hosts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_routes.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_routes_more.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_react_router.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_readers_writers_specs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_realtime.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_realtime_native.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_redact_salt.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_request_keys.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_route_specs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_route_tables.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_routes_search_explain.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_rpc.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_scip_health.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external_infra.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external_mail.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_snippet.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sockets.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_spring.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ssrf.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_starters_budget.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_surface.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_surface_transport.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_binding.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_exact.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_facts.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_modules.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_more.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_nav_values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_parse_errors.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_platform_files.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_props.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_sdk.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_testing.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_tauri_events.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_tests_scope.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_fields.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_file_specs.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_frameworks.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_node_http.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_nuxt.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_src_tsconfig.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_unguarded.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_update_check.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_values.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_viz.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_vue_nav.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_watch.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks_152.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks_152b.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_websockets.py +0 -0
- {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_workspace_link.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""code-graph: deterministic code knowledge graph (beta)."""
|
|
2
|
-
__version__ = "0.23.
|
|
2
|
+
__version__ = "0.23.11"
|
|
@@ -317,7 +317,8 @@ def main(argv=None):
|
|
|
317
317
|
p.add_argument("--json", action="store_true"); p.add_argument("--tests", action="store_true", help="include test code")
|
|
318
318
|
p = sub.add_parser("roundtrip", help="heuristic: writes of a stored property `Type.prop` through a lossy transform, "
|
|
319
319
|
"read back to seed UI state (#88)")
|
|
320
|
-
p.add_argument("spec"); p.add_argument("--db", required=True); p.add_argument("--json", action="store_true")
|
|
320
|
+
p.add_argument("spec", nargs="?"); p.add_argument("--db", required=True); p.add_argument("--json", action="store_true")
|
|
321
|
+
p.add_argument("--all", action="store_true", help="every stored property in one pass: only the round trips")
|
|
321
322
|
p.add_argument("--tests", action="store_true", help="include test code's reads and writes")
|
|
322
323
|
p = sub.add_parser("viz-plan", help="self-contained HTML overlay of a plan on the real graph")
|
|
323
324
|
p.add_argument("name"); p.add_argument("--db", required=True); p.add_argument("-o", "--out", required=True); p.add_argument("--plans-dir")
|
|
@@ -769,8 +770,14 @@ def main(argv=None):
|
|
|
769
770
|
print(json.dumps(res, indent=1) if a.json else LA.render(res))
|
|
770
771
|
elif a.cmd == "roundtrip":
|
|
771
772
|
from . import roundtrip as RT
|
|
772
|
-
|
|
773
|
-
|
|
773
|
+
if a.all or not a.spec:
|
|
774
|
+
if not a.all:
|
|
775
|
+
sys.exit("cg roundtrip: give a property `Type.prop`, or --all")
|
|
776
|
+
res = RT.roundtrip_all(st, include_tests=a.tests)
|
|
777
|
+
print(RT.to_json(res) if a.json else RT.render_all(res))
|
|
778
|
+
else:
|
|
779
|
+
res = RT.roundtrip(st, a.spec, include_tests=a.tests)
|
|
780
|
+
print(RT.to_json(res) if a.json else RT.render(res))
|
|
774
781
|
elif a.cmd in ("writers", "readers"):
|
|
775
782
|
groups = Q.access(st, a.spec, a.cmd, a.connection)
|
|
776
783
|
rows = [r for g in groups for r in g["rows"]]
|
|
@@ -61,6 +61,24 @@ def _input_dependent(text: str) -> bool:
|
|
|
61
61
|
t = re.sub(r"(?<![\w$])[A-Za-z_$][\w$]*\s*(?:<[^<>]*>)?\s*\(", " (", t) # call names
|
|
62
62
|
ids = re.findall(r"(?<![\w$])[A-Za-z_$][\w$]*", t)
|
|
63
63
|
return any(i.startswith("SELFREF_") or (i not in KEYWORDS and not i[0].isupper()) for i in ids)
|
|
64
|
+
_SUSP: dict = {} # file -> lines with a call of a Kotlin `suspend` function (an await point with no marker)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _load_suspend(st: GraphStore) -> None:
|
|
68
|
+
"""A plain call of a project `suspend` function inside `launch { }` (`val x = repo.load()`) suspends like
|
|
69
|
+
`.await()`: the indexed CALLS edges to nodes marked `suspend` give those lines."""
|
|
70
|
+
_SUSP.clear()
|
|
71
|
+
for r in st.q("""SELECT e.file, e.line FROM edges e JOIN nodes n ON n.id = e.dst
|
|
72
|
+
WHERE e.kind = 'CALLS' AND n.attrs LIKE '%"suspend": true%' AND e.file IS NOT NULL"""):
|
|
73
|
+
_SUSP.setdefault(r["file"], set()).add(r["line"])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _aw(t: str, file: str | None = None, line: int | None = None) -> bool:
|
|
77
|
+
"""Is there an await point in this line of source: a marker (`await`, `.await()`, `withContext`, `delay`) or,
|
|
78
|
+
in Kotlin, a call of a `suspend` function?"""
|
|
79
|
+
return bool(AWAIT_RE.search(t)) or (line is not None and line in _SUSP.get(file, ()))
|
|
80
|
+
|
|
81
|
+
|
|
64
82
|
TEST_PATH_RE = re.compile(r"(?:^|/)(?:tests?|__tests__|spec|testing|fixtures?|factories|Tests|UITests)/|"
|
|
65
83
|
r"[._-](?:test|spec)s?\.\w+:|Tests?\.swift:|Test\.kt:|(?:^|/)test_\w+\.py:")
|
|
66
84
|
|
|
@@ -104,6 +122,7 @@ def _opener(ls: list[str], line: int, lo: int):
|
|
|
104
122
|
|
|
105
123
|
|
|
106
124
|
def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -> list[dict]:
|
|
125
|
+
_load_suspend(st)
|
|
107
126
|
root = st.meta().get("root")
|
|
108
127
|
cache: dict = {}
|
|
109
128
|
fields = {r["id"]: json.loads(r["attrs"] or "{}") for r in st.q("SELECT id, attrs FROM nodes WHERE kind IN ('field','property')")}
|
|
@@ -122,7 +141,8 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
|
|
|
122
141
|
continue
|
|
123
142
|
kind, o = op
|
|
124
143
|
body = ls[o - 1:r["line"] - 1]
|
|
125
|
-
aw = next((o + i for i, t in enumerate(body) if
|
|
144
|
+
aw = next((o + i for i, t in enumerate(body) if _aw(t if i else t[t.find("{") + 1:] if "{" in t else t,
|
|
145
|
+
r["file"], o + i if i else None)), None)
|
|
126
146
|
if aw is None:
|
|
127
147
|
continue
|
|
128
148
|
before = "\n".join(ls[o - 1:aw - 1]) + "\n" + ls[aw - 1].split("await")[0]
|
|
@@ -131,7 +151,10 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
|
|
|
131
151
|
if GUARD_RE.search(after) or SINGLE_FLIGHT_RE.search(ls[o - 1]):
|
|
132
152
|
continue
|
|
133
153
|
aw_line = ls[aw - 1]
|
|
134
|
-
k =
|
|
154
|
+
k = aw_line.find("await")
|
|
155
|
+
if k < 0: # a Kotlin suspend call: the request is the right-hand side
|
|
156
|
+
m = re.search(r"(?<![=!<>])=(?!=)", aw_line)
|
|
157
|
+
k = m.end() if m else 0
|
|
135
158
|
stmt, depth = [], 0
|
|
136
159
|
for i in range(aw - 1, min(len(ls), aw + 5)):
|
|
137
160
|
x = ls[i][k:] if i == aw - 1 else ls[i]
|
|
@@ -144,10 +167,10 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
|
|
|
144
167
|
# the written value must come from the awaited result: a name bound on the await line (or derived from one
|
|
145
168
|
# after it), or the await itself on the write line; a loading flag reset after the await is not a stale result
|
|
146
169
|
res_vars = set()
|
|
147
|
-
for t in ls[aw - 1:r["line"] - 1]:
|
|
170
|
+
for j, t in enumerate(ls[aw - 1:r["line"] - 1]):
|
|
148
171
|
m = DECL_RE.search(t) or re.search(r"^\s*(?:self\.)?([A-Za-z_$][\w$]*)\s*=(?!=)", t)
|
|
149
172
|
nm = m and (m.group(1) or (m.group(2) if m.lastindex and m.lastindex >= 2 else None))
|
|
150
|
-
if nm and (
|
|
173
|
+
if nm and (_aw(t, r["file"], aw + j) or any(re.search(rf"(?<![\w$.]){re.escape(v)}\b", t.split("=", 1)[-1]) for v in res_vars)):
|
|
151
174
|
res_vars.add(nm)
|
|
152
175
|
wl = ls[r["line"] - 1]
|
|
153
176
|
if r["line"] - 1 == aw - 1:
|
|
@@ -182,7 +205,8 @@ LIFECYCLE_RE = re.compile(r"\.onAppear\b|\bonAppear\s*\(|\.task\s*[({]|\bonMount
|
|
|
182
205
|
CALLBACK_RE = re.compile(r"\.sink\s*\{|\.onReceive\s*\(|\.subscribe\s*[({]|\.collect(?:Latest)?\s*\{|\.observe\w*\s*\(|"
|
|
183
206
|
r"\bcompletion(?:Handler)?\s*:\s*\{|\{\s*\[?\s*(?:weak|unowned)\s+self\s*\]?|\.then\s*\(|\.on\s*\(\s*['\"]")
|
|
184
207
|
LITERAL_RHS_RE = re.compile(r"(?<![=!<>])=\s*(?:\[\s*\]|\[:\]|\{\s*\}|nil|null|None|undefined|0(?:\.0+)?[fFdDL]?|false|False|true|True|"
|
|
185
|
-
r"''|\"\"|emptyList\(\)|emptyMap\(\)|mutableListOf\(\)|\.init\(\)|\w+\(\)|\.\w
|
|
208
|
+
r"''|\"\"|emptyList\(\)|emptyMap\(\)|mutableListOf\(\)|\.init\(\)|\w+\(\)|\.\w+|"
|
|
209
|
+
r"[A-Z]\w*(?:\.[A-Z]\w*)+(?:\(\))?)\s*;?\s*$") # a sentinel: `AsyncData.Loading()`, `UiState.Idle`
|
|
186
210
|
LITERAL_ARG_RE = re.compile(r"\(\s*(?:nil|null|None|undefined|true|false|True|False|-?\d+(?:\.\d+)?|'[^']*'|\"[^\"]*\"|\[\s*\]|\{\s*\})\s*\)\s*;?\s*$")
|
|
187
211
|
INIT_NAMES = {"init", "__init__", "constructor", "<init>", "viewDidLoad", "onCreate", "initState", "mounted", "created", "setup"}
|
|
188
212
|
|
|
@@ -198,7 +222,7 @@ def _lifecycle(st, ls, r, node) -> str | None:
|
|
|
198
222
|
"""Is this write lifecycle code: an initializer with a real (non-default) value, or code directly inside
|
|
199
223
|
onAppear / .task (before any await) / onMounted / LaunchedEffect / useEffect / viewDidLoad?"""
|
|
200
224
|
t = ls[r["line"] - 1]
|
|
201
|
-
if
|
|
225
|
+
if _aw(t, r["file"], r["line"]):
|
|
202
226
|
return None
|
|
203
227
|
nm = ((node["name"] if node is not None else "") or "").split(".")[-1]
|
|
204
228
|
if nm in INIT_NAMES or (node is not None and node["kind"] == "class"):
|
|
@@ -212,7 +236,7 @@ def _lifecycle(st, ls, r, node) -> str | None:
|
|
|
212
236
|
lo = max(1, r["line"] - 6)
|
|
213
237
|
for i in range(r["line"], lo - 1, -1):
|
|
214
238
|
x = ls[i - 1]
|
|
215
|
-
if i < r["line"] and
|
|
239
|
+
if i < r["line"] and _aw(x, r["file"], i):
|
|
216
240
|
return None
|
|
217
241
|
m = LIFECYCLE_RE.search(x)
|
|
218
242
|
if m:
|
|
@@ -225,7 +249,8 @@ def _async_callback(ls, r, lo) -> str | None:
|
|
|
225
249
|
op = _opener(ls, r["line"], lo)
|
|
226
250
|
if op is not None:
|
|
227
251
|
body = ls[op[1] - 1:r["line"] - 1]
|
|
228
|
-
if any(
|
|
252
|
+
if any(_aw(x, r["file"], op[1] + j) for j, x in enumerate(body) if j) or \
|
|
253
|
+
(body and AWAIT_RE.search(body[0][body[0].find("{") + 1:])):
|
|
229
254
|
return f"after await in {op[0]}"
|
|
230
255
|
for i in range(r["line"] - 1, max(lo, r["line"] - 8) - 1, -1):
|
|
231
256
|
m = CALLBACK_RE.search(ls[i - 1]) if i >= 1 else None
|
|
@@ -235,6 +260,7 @@ def _async_callback(ls, r, lo) -> str | None:
|
|
|
235
260
|
|
|
236
261
|
|
|
237
262
|
def two_writers(st: GraphStore, include_tests: bool = False) -> list[dict]:
|
|
263
|
+
_load_suspend(st)
|
|
238
264
|
root = st.meta().get("root")
|
|
239
265
|
cache: dict = {}
|
|
240
266
|
rows = st.q("""SELECT e.src, e.dst, e.file, e.line, e.attrs, n.line AS fl FROM edges e JOIN nodes n ON n.id = e.src
|
|
@@ -361,6 +387,14 @@ def incomplete_cache_key(st: GraphStore, include_tests: bool = False) -> list[di
|
|
|
361
387
|
sig = "\n".join(body[:3])
|
|
362
388
|
sig = sig[sig.find("("):] if "(" in sig else ""
|
|
363
389
|
params = _idents(_split_args(sig[1:]) and ",".join(re.sub(r"[:=].*", "", a, flags=re.S) for a in _split_args(sig[1:])) or "")
|
|
390
|
+
# instance fields used without `self.` / `this.` (Swift, Kotlin): the READS_PROP edges bind them
|
|
391
|
+
own = {e["dst"].rsplit(".", 1)[-1] for e in st.q("SELECT dst, attrs FROM edges WHERE kind = 'READS_PROP' AND src = ?",
|
|
392
|
+
(fn["id"],))
|
|
393
|
+
if json.loads(e["attrs"] or "{}").get("receiver") in (None, "self", "this")}
|
|
394
|
+
text = "\n".join(body)
|
|
395
|
+
# ... unless the field is an object the function calls (`client.get(…)`, `mediaLoader.load(…)`): a dependency,
|
|
396
|
+
# not an input of the cached value
|
|
397
|
+
own = {f for f in own if not re.search(rf"(?<![\w$.]){re.escape(f)}\s*[?!]?\.\s*\w+\s*[({{]", text)}
|
|
364
398
|
assigns: dict = {}
|
|
365
399
|
for off, t in enumerate(body):
|
|
366
400
|
m = re.search(r"\b(?:let|var|val|const)\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=(?!=)(.*)|^\s*([a-z_$][\w$]*)\s*=(?!=)(.*)", t)
|
|
@@ -384,8 +418,10 @@ def incomplete_cache_key(st: GraphStore, include_tests: bool = False) -> list[di
|
|
|
384
418
|
vb = val.strip().rstrip(";")
|
|
385
419
|
if re.fullmatch(r"[A-Za-z_$][\w$]*", vb) and vb not in assigns:
|
|
386
420
|
continue # `put(key, value)` passes a value through; nothing is computed here
|
|
387
|
-
|
|
388
|
-
|
|
421
|
+
bare = lambda ids: {f"self_{i}" if i in own and i not in params and i not in assigns else i # noqa: E731
|
|
422
|
+
for i in ids}
|
|
423
|
+
kids = bare(_expand(_idents(key), assigns))
|
|
424
|
+
vids = bare(_expand(_idents(val), assigns)) - {vb}
|
|
389
425
|
setter = re.match(r"(?:set|put|insert|store|save|cache|add|update|write|remember|record)(?![a-z])",
|
|
390
426
|
(fn["fqn"] or "").split(".")[-1].split("(")[0])
|
|
391
427
|
inputs = {i for i in vids if (i in params and not setter and not PAYLOAD_RE.fullmatch(i.lstrip("$")))
|
|
@@ -421,6 +457,7 @@ def echo_suppression(st: GraphStore, include_tests: bool = False, patterns: list
|
|
|
421
457
|
programmatic writes of some state so the change observer, which checks the guard and returns early, does not
|
|
422
458
|
echo them back. Reports state of that pattern that is also written from an async path / callback *without*
|
|
423
459
|
setting the guard: that write reaches the observer and is sent back (a loop or a clobbered edit)."""
|
|
460
|
+
_load_suspend(st)
|
|
424
461
|
root = st.meta().get("root")
|
|
425
462
|
cache: dict = {}
|
|
426
463
|
guards = [r["id"] for r in st.q("SELECT id, name FROM nodes WHERE kind = 'field'") if GUARD_FIELD_RE.fullmatch((r["name"] or "").rsplit(".", 1)[-1].replace("_", ""))]
|
|
@@ -877,13 +877,16 @@ def _access_text(st, spec: str, what: str, limit: int, connection: str | None =
|
|
|
877
877
|
|
|
878
878
|
|
|
879
879
|
@tool
|
|
880
|
-
def roundtrip(prop: str, include_tests: bool = False) -> str:
|
|
880
|
+
def roundtrip(prop: str = "", include_tests: bool = False) -> str:
|
|
881
881
|
"""Heuristic (#88): does a stored property `Type.prop` round-trip through a lossy transform? Each write site with
|
|
882
882
|
the lossy call it passes through (clamp, min/max, round, truncating casts, `fit*`, `.cg.yaml` `lossy:` names,
|
|
883
883
|
`@cg-lossy` functions; within the function plus one hop through direct callers), each read site that seeds UI
|
|
884
884
|
state (init, onAppear, remember, useState(initial), mounted), and un-narrowed ranges drawn next to such a read.
|
|
885
|
-
Findings are heuristic evidence with file:line, not proof.
|
|
885
|
+
Findings are heuristic evidence with file:line, not proof. An empty `prop` (or `*`) checks every stored property
|
|
886
|
+
in one pass and lists only the round trips."""
|
|
886
887
|
from . import roundtrip as RT
|
|
888
|
+
if prop.strip() in ("", "*"):
|
|
889
|
+
return RT.render_all(RT.roundtrip_all(_st(), include_tests=include_tests))
|
|
887
890
|
return RT.render(RT.roundtrip(_st(), prop, include_tests=include_tests))
|
|
888
891
|
|
|
889
892
|
|
|
@@ -56,6 +56,11 @@ RES = re.compile(r"""\b(?:UI|NS)?Image\(\s*(?:named:\s*)?"([^"\n]+)"|\bAsset\.(?
|
|
|
56
56
|
r"""\bImage\(\s*\.(\w+)\s*\)|\b(?:R|Res)\.(?:drawable|mipmap|raw)\.(\w+)""")
|
|
57
57
|
WIRE = re.compile(r'@(?:SerialName|SerializedName|JsonProperty|Json\(\s*name\s*=)\s*\(?\s*(?:value\s*=\s*)?"([^"\n]+)"')
|
|
58
58
|
RAW_CASE = re.compile(r'\bcase\s+`?(\w+)`?\s*=\s*"([^"\n]+)"')
|
|
59
|
+
ARM = re.compile(r"^(\s*)case\s+(?:let\s+|var\s+)?((?:\.\w+(?:\([^)]*\))?\s*,?\s*)+)(?:where\b[^:]*)?:")
|
|
60
|
+
DISPATCH = {"receive", "perform", "handle", "handleaction", "onaction", "reduce", "send", "dispatch", "process"}
|
|
61
|
+
MIN_MEMBER_SCORE = 0.35
|
|
62
|
+
MIN_NAME_SCORE = 0.45
|
|
63
|
+
MIN_BOTH_SCORE = 0.25
|
|
59
64
|
EDGE_KINDS = ("CALLS", "INSTANTIATES", "NAVIGATES_TO", "HTTP_CALLS", "USES_VALUE", "REFERENCES_FN", "USES_TYPE")
|
|
60
65
|
|
|
61
66
|
|
|
@@ -140,9 +145,11 @@ class Side:
|
|
|
140
145
|
self.by_file[r["file"]].append((r["line"], r["end_line"] or r["line"], r["id"]))
|
|
141
146
|
self.span[r["id"]] = (r["file"], r["line"], r["end_line"] or r["line"])
|
|
142
147
|
self.out = defaultdict(list)
|
|
143
|
-
|
|
148
|
+
self.out_at = defaultdict(list) # src -> [(line, dst, kind)]
|
|
149
|
+
q = f"SELECT src, dst, kind, line FROM edges WHERE kind IN ({','.join('?' * len(EDGE_KINDS))})"
|
|
144
150
|
for r in c.execute(q, EDGE_KINDS):
|
|
145
151
|
self.out[r["src"]].append((r["dst"], r["kind"]))
|
|
152
|
+
self.out_at[r["src"]].append((r["line"] or 0, r["dst"], r["kind"]))
|
|
146
153
|
self.http = {r["id"]: (r["name"] or r["id"]) for r in c.execute("SELECT id, name FROM nodes WHERE kind='http'")}
|
|
147
154
|
self.decl = defaultdict(list) # owner fqn -> [(name, kind, file, line)] of stored fields / enum cases
|
|
148
155
|
for r in c.execute("SELECT kind, fqn, file, line, attrs FROM nodes WHERE kind IN ('field', 'enum_case') "
|
|
@@ -202,21 +209,50 @@ class Side:
|
|
|
202
209
|
for la, lb, nid in self.by_file.get(file, ()):
|
|
203
210
|
if a <= la <= b:
|
|
204
211
|
for dst, kind in self.out.get(nid, ()):
|
|
205
|
-
|
|
206
|
-
p = re.sub(r"\{[^}]*\}|(?<=/):\w+", "{}", self.http.get(dst, dst).split(" ")[-1])
|
|
207
|
-
f.add("http:" + p.lstrip("/").lower())
|
|
208
|
-
continue
|
|
209
|
-
dq = self.fqn_of.get(dst)
|
|
210
|
-
t = rename(dq)
|
|
211
|
-
if t:
|
|
212
|
-
f.add("sym:" + t)
|
|
213
|
-
# the called member's own name (two words or more): `getDevices` on an AuthService and on
|
|
214
|
-
# an AuthRepository is the same call even when the owners are not paired
|
|
215
|
-
nm = _short(dq or "").split("(")[0]
|
|
216
|
-
if kind == "CALLS" and len(_words(nm)) >= 2:
|
|
217
|
-
f.add("call:" + re.sub(r"^(get|fetch|load)(?=[a-z])", "", _fold(nm)))
|
|
212
|
+
self._edge(f, dst, kind, rename)
|
|
218
213
|
return f
|
|
219
214
|
|
|
215
|
+
def _edge(self, f: set, dst: str, kind: str, rename) -> None:
|
|
216
|
+
if kind == "HTTP_CALLS" or dst in self.http:
|
|
217
|
+
p = re.sub(r"\{[^}]*\}|(?<=/):\w+", "{}", self.http.get(dst, dst).split(" ")[-1])
|
|
218
|
+
f.add("http:" + p.lstrip("/").lower())
|
|
219
|
+
return
|
|
220
|
+
dq = self.fqn_of.get(dst)
|
|
221
|
+
t = rename(dq)
|
|
222
|
+
if t:
|
|
223
|
+
f.add("sym:" + t)
|
|
224
|
+
# the called member's own name (two words or more): `getDevices` on an AuthService and on
|
|
225
|
+
# an AuthRepository is the same call even when the owners are not paired
|
|
226
|
+
nm = _short(dq or "").split("(")[0]
|
|
227
|
+
if kind == "CALLS" and len(_words(nm)) >= 2:
|
|
228
|
+
f.add("call:" + re.sub(r"^(get|fetch|load)(?=[a-z])", "", _fold(nm)))
|
|
229
|
+
|
|
230
|
+
def arms(self, sym: dict) -> list:
|
|
231
|
+
"""The top-level `case .x:` arms of a Swift `receive(_:)` / `perform(_:)` style switch: [(case names,
|
|
232
|
+
features of what the arm calls)]."""
|
|
233
|
+
file, a, b = self.span.get(sym["id"], (None, 0, 0))
|
|
234
|
+
if not file or b <= a:
|
|
235
|
+
return []
|
|
236
|
+
rows = self.text(file, a, b).split("\n")
|
|
237
|
+
hits = [(i, len(m.group(1)), m.group(2)) for i, ln in enumerate(rows) for m in [ARM.match(ln)] if m]
|
|
238
|
+
if not hits:
|
|
239
|
+
return []
|
|
240
|
+
top = min(h[1] for h in hits)
|
|
241
|
+
hits = [h for h in hits if h[1] == top]
|
|
242
|
+
out = []
|
|
243
|
+
for k, (i, _, names) in enumerate(hits):
|
|
244
|
+
lo, hi = a + i, (a + hits[k + 1][0] - 1 if k + 1 < len(hits) else b)
|
|
245
|
+
f = set()
|
|
246
|
+
for ln, dst, kind in self.out_at.get(sym["id"], ()):
|
|
247
|
+
if lo <= ln <= hi:
|
|
248
|
+
self._edge(f, dst, kind, lambda x: x)
|
|
249
|
+
for la, lb, nid in self.by_file.get(file, ()):
|
|
250
|
+
if lo <= la <= hi and nid != sym["id"]:
|
|
251
|
+
for dst, kind in self.out.get(nid, ()):
|
|
252
|
+
self._edge(f, dst, kind, lambda x: x)
|
|
253
|
+
out.append((re.findall(r"\.(\w+)", names), f))
|
|
254
|
+
return out
|
|
255
|
+
|
|
220
256
|
|
|
221
257
|
def match(S: dict, T: dict, out: dict, sym_of: dict, cat_of: dict, t_syms: dict, t_cat: dict, learn: bool = True) -> dict:
|
|
222
258
|
ui_s = {fq: is_ui(fq, sym, S["members"]) for fq, sym in sym_of.items()}
|
|
@@ -369,7 +405,8 @@ def _structure(S, T, out, sym_of, cat_of, t_syms, t_cat, used):
|
|
|
369
405
|
if sym is not None:
|
|
370
406
|
src_f[row["symbol"]] = sS.features(sym, lambda x: pairs.get(x)) | name_words(row["symbol"])
|
|
371
407
|
tgt_f = {fq: sT.features(sym, lambda x: x) | name_words(fq) for fq, sym in t_syms.items()
|
|
372
|
-
if fq not in used and not PREVIEW.search(_short(fq))
|
|
408
|
+
if fq not in used and not PREVIEW.search(_short(fq))
|
|
409
|
+
and not (_fold(_short(fq).split("(")[0]) in DISPATCH and sT.arms(sym))} # its arms are paired below
|
|
373
410
|
src_f = {fq: f for fq, f in src_f.items() if not PREVIEW.search(_short(fq))}
|
|
374
411
|
df = Counter()
|
|
375
412
|
for fs in list(src_f.values()) + list(tgt_f.values()):
|
|
@@ -423,4 +460,88 @@ def _structure(S, T, out, sym_of, cat_of, t_syms, t_cat, used):
|
|
|
423
460
|
else:
|
|
424
461
|
keep.append(row)
|
|
425
462
|
out["missing"] = keep
|
|
463
|
+
_members(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T)
|
|
426
464
|
_by_fields(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _members(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T):
|
|
468
|
+
"""Members of matched types still missing, paired inside the counterpart type (#227): with an unmatched member,
|
|
469
|
+
or a top-level function, that makes the same paired calls, or with a `case .x:` arm of its `receive(_:)` /
|
|
470
|
+
`perform(_:)` switch (`handleCopyClick` on a view model, `case .copyGeneratedValue:` in the processor). Uses are
|
|
471
|
+
weighed within the pair of types, so a call that is common across the project still tells two members apart."""
|
|
472
|
+
pairs = {r["symbol"]: r["target"] for r in out["matched"]}
|
|
473
|
+
by_owner = defaultdict(list)
|
|
474
|
+
for r in out["missing"]:
|
|
475
|
+
if r.get("owner_matched") and r["symbol"] in sym_of and not PREVIEW.search(_short(r["symbol"])):
|
|
476
|
+
by_owner[r["owner_matched"]].append(r)
|
|
477
|
+
if not by_owner:
|
|
478
|
+
return
|
|
479
|
+
ident = lambda x: x # noqa: E731
|
|
480
|
+
top, top_inv = None, defaultdict(list)
|
|
481
|
+
taken_arms, best, cand_of = set(), {}, {}
|
|
482
|
+
for o, rows in by_owner.items():
|
|
483
|
+
units = [] # (key, target fqn, case name or None, category, features)
|
|
484
|
+
for nm, sym in T["members"].get(o, {}).items():
|
|
485
|
+
fq = f"{o}.{nm}"
|
|
486
|
+
arms = sT.arms(sym) if _fold(nm.split("(")[0]) in DISPATCH else []
|
|
487
|
+
for names, f in arms: # a dispatching switch stands for its arms, not as a whole
|
|
488
|
+
ws = set().union(*(name_words(n) for n in names))
|
|
489
|
+
units.append(((fq, names[0]), fq, names[0], "func", f | ws))
|
|
490
|
+
if not arms and fq not in used and not PREVIEW.search(nm):
|
|
491
|
+
units.append((fq, fq, None, t_cat.get(fq), sT.features(sym, ident) | name_words(fq)))
|
|
492
|
+
src = {r["symbol"]: sS.features(sym_of[r["symbol"]], pairs.get) | name_words(r["symbol"]) for r in rows}
|
|
493
|
+
if top is None: # top-level functions, met only through two paired uses
|
|
494
|
+
top = {}
|
|
495
|
+
for fq, sym in t_syms.items():
|
|
496
|
+
if t_cat.get(fq) == "func" and fq not in used and "." not in fq.replace(o, "") and fq in T["top"]:
|
|
497
|
+
top[fq] = {f for f in sT.features(sym, ident) if f.startswith("sym:")} | name_words(fq)
|
|
498
|
+
for f in top[fq]:
|
|
499
|
+
if f.startswith("sym:"):
|
|
500
|
+
top_inv[f].append(fq)
|
|
501
|
+
df = Counter()
|
|
502
|
+
for fs in list(src.values()) + [u[4] for u in units]:
|
|
503
|
+
df.update(fs)
|
|
504
|
+
n = len(src) + len(units)
|
|
505
|
+
for fq, fs in src.items():
|
|
506
|
+
extra = {t for f in fs if f.startswith("sym:") for t in top_inv.get(f, ())}
|
|
507
|
+
cands = [u for u in units if u[3] == cat_of.get(fq)] + [(t, t, None, "func", top[t]) for t in extra
|
|
508
|
+
if cat_of.get(fq) == "func"]
|
|
509
|
+
scored = []
|
|
510
|
+
for key, t, case, cat, tf in cands:
|
|
511
|
+
sh = fs & tf
|
|
512
|
+
uses = sum(1 for f in sh if not f.startswith("w:"))
|
|
513
|
+
words = len(sh) - uses
|
|
514
|
+
if case is None and t in top and uses < 2:
|
|
515
|
+
continue
|
|
516
|
+
if not (uses >= 2 or (uses and words) or words >= 2):
|
|
517
|
+
continue
|
|
518
|
+
wt = lambda f: math.log(1 + n / df[f]) if df[f] else math.log(1 + n) # noqa: E731
|
|
519
|
+
sc = sum(wt(f) for f in sh) / math.sqrt(sum(wt(f) for f in fs) * sum(wt(f) for f in tf))
|
|
520
|
+
if sc < (MIN_NAME_SCORE if not uses else MIN_BOTH_SCORE if words else MIN_MEMBER_SCORE):
|
|
521
|
+
continue # names alone must agree more; a use and a name word less
|
|
522
|
+
scored.append((sc, key, t, case, sh))
|
|
523
|
+
scored.sort(key=lambda x: -x[0])
|
|
524
|
+
if scored and (len(scored) == 1 or scored[1][0] < MARGIN * scored[0][0]):
|
|
525
|
+
best[fq] = scored[0]
|
|
526
|
+
by_key = defaultdict(list)
|
|
527
|
+
for fq, (sc, key, *_rest) in best.items():
|
|
528
|
+
by_key[key].append((sc, fq))
|
|
529
|
+
keep = []
|
|
530
|
+
for row in out["missing"]:
|
|
531
|
+
got = best.get(row["symbol"])
|
|
532
|
+
if not got:
|
|
533
|
+
keep.append(row)
|
|
534
|
+
continue
|
|
535
|
+
sc, key, t, case, sh = got
|
|
536
|
+
if max(by_key[key])[1] != row["symbol"] or (case is None and t in used) or key in taken_arms:
|
|
537
|
+
keep.append(row)
|
|
538
|
+
continue
|
|
539
|
+
if case is None:
|
|
540
|
+
used.add(t)
|
|
541
|
+
taken_arms.add(key)
|
|
542
|
+
ev = sorted(sh, key=lambda f: (f.startswith("w:"), f))[:6]
|
|
543
|
+
r = dict(row, target=t, confidence="structure", score=round(sc, 2), evidence=([f"case .{case}"] if case else []) + ev)
|
|
544
|
+
if case:
|
|
545
|
+
r["case"] = case
|
|
546
|
+
out["matched"].append(r)
|
|
547
|
+
out["missing"] = keep
|
|
@@ -207,6 +207,12 @@ def _suspend_lambdas(src: bytes) -> tuple[bytes, int]:
|
|
|
207
207
|
# `get<T>` the whitespace character in front of `get` / `set` becomes `;`. That ends the declaration and keeps every
|
|
208
208
|
# byte offset.
|
|
209
209
|
_ROUTE_ACCESSOR = re.compile(rb"([ \t]*)[ \t]((?:get|set)\s*(?:\(\s*(?:\"|\"\"\")|\{|<))")
|
|
210
|
+
# `var x by remember { mutableStateOf(..) }`, `by rememberSaveable { .. }`, `by mutableStateOf(..)`,
|
|
211
|
+
# `by vm.state.collectAsState()` / `collectAsStateWithLifecycle()`, `by produceState(..)`, `by derivedStateOf { }`
|
|
212
|
+
LOCAL_STATE = re.compile(r"\b(var|val)\s+(\w+)(?:\s*:\s*[^=\n]+?)?\s+by\s+(?:remember(?:Saveable)?\b[^\n{]*\{\s*"
|
|
213
|
+
r"(mutable\w*StateOf|derivedStateOf|mutableStateListOf|mutableStateMapOf)"
|
|
214
|
+
r"|(mutable\w*StateOf|derivedStateOf)\s*[({]|[\w.]*?\b(collectAs\w*State\w*)\s*\("
|
|
215
|
+
r"|(produceState|observeAsState|subscribeAsState)\b|[\w.]*?\.(observeAsState|subscribeAsState)\s*\()")
|
|
210
216
|
_DECL_LINE = re.compile(rb"^\s*(?:(?:private|internal|public|protected|const|lateinit|override|open)\s+)*(?:val|var)\s")
|
|
211
217
|
|
|
212
218
|
|
|
@@ -321,6 +327,7 @@ class KotlinPlugin(LanguagePlugin):
|
|
|
321
327
|
# stored properties of a class / enum are `field:<Type>.<name>` nodes with READS_PROP / WRITES_PROP (#88)
|
|
322
328
|
self.fields: dict[str, dict[str, str]] = defaultdict(dict) # class fqn -> name -> field id
|
|
323
329
|
self.field_names: set[str] = set()
|
|
330
|
+
self.local_state: dict[str, dict[str, str]] = defaultdict(dict) # function id -> local state name -> field id
|
|
324
331
|
self.copies: list[tuple] = [] # (owner, receiver (type, text), [(arg name, line)], kf, decl): `x.copy(a = 1)`
|
|
325
332
|
self.frefs: list[tuple] = [] # (owner_id, name, receiver (type, text) | None, line, file, decl, write)
|
|
326
333
|
self.reads: list[tuple] = [] # (owner_id, name, receiver node, line, file obj, decl, read | write)
|
|
@@ -617,6 +624,7 @@ class KotlinPlugin(LanguagePlugin):
|
|
|
617
624
|
if any(dflt for _n, dflt, _f in fparams):
|
|
618
625
|
self.fn_params[dc.id] = fparams
|
|
619
626
|
self._add_decl(dc, kind)
|
|
627
|
+
self._local_state(dc, kf, c)
|
|
620
628
|
self._local_fns(c, kf, dc)
|
|
621
629
|
elif ty == "enum_entry" and cls is not None:
|
|
622
630
|
nm = self._name(c)
|
|
@@ -655,6 +663,30 @@ class KotlinPlugin(LanguagePlugin):
|
|
|
655
663
|
self.field_names.add(nm)
|
|
656
664
|
self.st["stored_property_nodes"] += 1
|
|
657
665
|
|
|
666
|
+
def _local_state(self, dc: Decl, kf: KFile, c):
|
|
667
|
+
"""Compose state held in a local delegate (`var count by remember { mutableIntStateOf(0) }`, `val ui by
|
|
668
|
+
vm.state.collectAsStateWithLifecycle()`): a `field:<function>.<name>` node (`property: state`, `hook`) whose
|
|
669
|
+
reads and writes inside the function are READS_PROP / WRITES_PROP, like React `useState` (#105, #88)."""
|
|
670
|
+
txt = self.t(c)
|
|
671
|
+
if " by " not in txt:
|
|
672
|
+
return
|
|
673
|
+
for m in LOCAL_STATE.finditer(txt):
|
|
674
|
+
nm = m.group(2)
|
|
675
|
+
if nm in self.local_state[dc.id]:
|
|
676
|
+
continue
|
|
677
|
+
hook = next(g for g in m.groups()[2:] if g).split(".")[-1]
|
|
678
|
+
line = c.start_point[0] + 1 + txt.count("\n", 0, m.start(2))
|
|
679
|
+
a = {"property": "state", "hook": hook, "binding": m.group(1), "local": True}
|
|
680
|
+
if kf.test:
|
|
681
|
+
a["test"] = True
|
|
682
|
+
fid = self.b.add_node("field", f"{dc.fqn}.{nm}", name=nm, fqn=f"{dc.fqn}.{nm}", file=kf.rel, line=line,
|
|
683
|
+
end_line=line, module=getattr(self.b.nodes.get(dc.id), "module", None), lang="kotlin",
|
|
684
|
+
attrs=a)
|
|
685
|
+
self.b.add_edge(dc.id, fid, "CONTAINS", kf.rel, line, EXACT)
|
|
686
|
+
self.local_state[dc.id][nm] = fid
|
|
687
|
+
self.field_names.add(nm)
|
|
688
|
+
self.st["local_state_nodes"] += 1
|
|
689
|
+
|
|
658
690
|
def _prop_kind(self, c) -> tuple | None:
|
|
659
691
|
"""(property kind, accessors) of a property that runs code when read or written (#89): a custom `get()` /
|
|
660
692
|
`set(value)` (custom), `by lazy { }` (lazy) or another delegate (delegated); None for a stored property."""
|
|
@@ -1954,6 +1986,13 @@ class KotlinPlugin(LanguagePlugin):
|
|
|
1954
1986
|
decl if decl is not None and decl.kind == "class" else None)
|
|
1955
1987
|
fid = None
|
|
1956
1988
|
rt = recv[1] if recv is not None else None
|
|
1989
|
+
loc = self.local_state.get(decl.id, {}).get(name) if recv is None and decl is not None else None
|
|
1990
|
+
if loc:
|
|
1991
|
+
if loc != owner:
|
|
1992
|
+
self.b.add_edge(owner, loc, "WRITES_PROP" if write else "READS_PROP", kf.rel, line, RESOLVED,
|
|
1993
|
+
receiver="local", **({"via": write} if isinstance(write, str) else {}))
|
|
1994
|
+
self.st["local_state_writes" if write else "local_state_reads"] += 1
|
|
1995
|
+
continue
|
|
1957
1996
|
if recv is None or rt == "this":
|
|
1958
1997
|
if recv is None and self._shadowed_local(decl, name, kf):
|
|
1959
1998
|
continue
|
{cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/extract.mjs
RENAMED
|
@@ -983,6 +983,7 @@ const STATE_HOOKS = new Set(['useState', 'useReducer', 'ref', 'shallowRef', 'rea
|
|
|
983
983
|
const optFields = new Map() // options ObjectLiteralExpression -> Map(name -> field id)
|
|
984
984
|
const storeFields = new Map() // store VariableDeclaration -> Map(name -> field id)
|
|
985
985
|
const optNames = new Set()
|
|
986
|
+
const fileOptData = new Map() // rel .vue path -> Map(data() key -> field id): bare names in its template
|
|
986
987
|
function returnedObject(f) {
|
|
987
988
|
if (!f) return null
|
|
988
989
|
f = unwrap(f)
|
|
@@ -1013,6 +1014,7 @@ function optionsFields(sf, r, fid) {
|
|
|
1013
1014
|
}
|
|
1014
1015
|
optFields.set(obj, m)
|
|
1015
1016
|
if (storeDecl) storeFields.set(storeDecl, m)
|
|
1017
|
+
if (hook === 'data') { const f = fileOptData.get(r) || new Map(); for (const [k, v] of m) f.set(k, v); fileOptData.set(r, f) }
|
|
1016
1018
|
}
|
|
1017
1019
|
const scan = (n) => {
|
|
1018
1020
|
if (ts.isCallExpression(n) && ts.isIdentifier(n.expression) && n.expression.text === 'defineStore') {
|
|
@@ -1065,6 +1067,20 @@ function optionsRef(node, cur, sf, r) {
|
|
|
1065
1067
|
addEdge(cur, fid, write ? 'WRITES_PROP' : 'READS_PROP', r, lineOf(node, sf), m === null ? 'resolved' : (recv === 'this' ? 'exact' : 'resolved'), { receiver: recv, ...(via ? { via } : {}) })
|
|
1066
1068
|
stats[write ? 'state_field_writes' : 'state_field_reads'] = (stats[write ? 'state_field_writes' : 'state_field_reads'] || 0) + 1
|
|
1067
1069
|
}
|
|
1070
|
+
// `{{ count }}` / `@click="count++"` in the template of an Options API component: a bare data() key (#88)
|
|
1071
|
+
function optionsTplRef(node, cur, sf, r) {
|
|
1072
|
+
const m = fileOptData.get(r), fid = m && m.get(node.text)
|
|
1073
|
+
if (!fid || fid === cur || !isTemplateLine(node, sf)) return
|
|
1074
|
+
const p = node.parent
|
|
1075
|
+
if (p && ((ts.isPropertyAccessExpression(p) && p.name === node) || (ts.isPropertyAssignment(p) && p.name === node)
|
|
1076
|
+
|| ts.isParameter(p) || ts.isFunctionDeclaration(p))) return
|
|
1077
|
+
let s = null
|
|
1078
|
+
try { s = checker.getSymbolAtLocation(node) } catch { }
|
|
1079
|
+
if (s && (s.declarations || []).length) return // a v-for variable / slot prop / import of that name
|
|
1080
|
+
const [write, via] = writeOf(node)
|
|
1081
|
+
addEdge(cur, fid, write ? 'WRITES_PROP' : 'READS_PROP', r, lineOf(node, sf), 'resolved', { template: true, ...(via ? { via } : {}) })
|
|
1082
|
+
stats[write ? 'state_field_writes' : 'state_field_reads'] = (stats[write ? 'state_field_writes' : 'state_field_reads'] || 0) + 1
|
|
1083
|
+
}
|
|
1068
1084
|
function stateDecl(d, cur, sf, r) {
|
|
1069
1085
|
const init = unwrap(d.initializer)
|
|
1070
1086
|
if (!init || !ts.isCallExpression(init)) return
|
|
@@ -2865,6 +2881,7 @@ for (const sf of sourceFiles) {
|
|
|
2865
2881
|
if (ts.isVariableDeclaration(node) && node.initializer && cur) stateDecl(node, cur, sf, r)
|
|
2866
2882
|
if (ts.isIdentifier(node) && stateNames.has(node.text) && cur) stateRef(node, cur, sf, r)
|
|
2867
2883
|
if (ts.isPropertyAccessExpression(node) && optNames.has(node.name.text) && cur) optionsRef(node, cur, sf, r)
|
|
2884
|
+
if (isVue && ts.isIdentifier(node) && optNames.has(node.text) && cur && fileOptData.has(r)) optionsTplRef(node, cur, sf, r)
|
|
2868
2885
|
// .astro: `<a href>` inside a template expression (`{posts.map(p => <a href={`/blog/${p.slug}`}>)}`) is navigation,
|
|
2869
2886
|
// like a top-level one (the scanner handles those)
|
|
2870
2887
|
if (isAstro && ts.isJsxAttribute(node) && node.initializer && ts.isIdentifier(node.name) && node.name.text === 'href'
|
|
@@ -119,6 +119,13 @@ def _bounds(text: str) -> tuple[float, float] | None:
|
|
|
119
119
|
|
|
120
120
|
def _fn_bounds(st: GraphStore, src: _Src, name: str) -> tuple[float, float] | None:
|
|
121
121
|
"""Bounds inside a project function `name` (`func clamp(_ v: Double) -> Double { min(max(v, 0), 10) }`)."""
|
|
122
|
+
memo = src.__dict__.setdefault("bounds", {})
|
|
123
|
+
if name not in memo:
|
|
124
|
+
memo[name] = _fn_bounds1(st, src, name)
|
|
125
|
+
return memo[name]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _fn_bounds1(st: GraphStore, src: _Src, name: str) -> tuple[float, float] | None:
|
|
122
129
|
for r in st.q("SELECT file, line, end_line FROM nodes WHERE kind IN ('function','method') AND name=? LIMIT 3", (name,)):
|
|
123
130
|
ls = src.lines(r["file"])
|
|
124
131
|
body = "\n".join(ls[(r["line"] or 1) - 1:(r["end_line"] or r["line"] or 1)])
|
|
@@ -273,8 +280,19 @@ def roundtrip(st: GraphStore, spec: str, include_tests: bool = False) -> dict:
|
|
|
273
280
|
for r in Q.prop_access(st, spec, "READS_PROP"):
|
|
274
281
|
if include_tests or not r.get("test"):
|
|
275
282
|
res["reads"].append(_read_site(st, src, r))
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
res["findings"] = _pair(src, res["writes"], res["reads"], prop)
|
|
284
|
+
for x in res["writes"]:
|
|
285
|
+
x.pop("_span", None)
|
|
286
|
+
for x in res["reads"]:
|
|
287
|
+
x.pop("_src", None)
|
|
288
|
+
return res
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _pair(src, writes, reads, prop) -> list[dict]:
|
|
292
|
+
"""Findings: a lossy write and a read of the same property that seeds UI state."""
|
|
293
|
+
out = []
|
|
294
|
+
lossy = [w for w in writes if w["lossy"]]
|
|
295
|
+
seeds = [x for x in reads if x["seeds"]]
|
|
278
296
|
for w in lossy:
|
|
279
297
|
b = w["lossy"].get("bounds")
|
|
280
298
|
for x in seeds:
|
|
@@ -287,14 +305,60 @@ def roundtrip(st: GraphStore, spec: str, include_tests: bool = False) -> dict:
|
|
|
287
305
|
"read": x["at"], "seeds": x["seeds"], "wider_ranges": []}
|
|
288
306
|
if b:
|
|
289
307
|
f["wider_ranges"] = _ranges(src, x["at"].rsplit(":", 1)[0], tuple(b))[:5]
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
308
|
+
out.append(f)
|
|
309
|
+
return out
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def roundtrip_all(st: GraphStore, include_tests: bool = False) -> dict:
|
|
313
|
+
"""Every stored property in one pass (`cg roundtrip --all`): write sites are read once, file lines and callee
|
|
314
|
+
bounds are shared, and reads are looked at only for properties with a lossy write."""
|
|
315
|
+
root = st.meta().get("root")
|
|
316
|
+
globs, lsrc = lossy_names(st, root)
|
|
317
|
+
src = _Src(root)
|
|
318
|
+
stored = {r["id"]: r["fqn"] or r["name"] or r["id"] for r in st.q(
|
|
319
|
+
"""SELECT id, fqn, name FROM nodes WHERE kind IN ('field', 'property') AND attrs LIKE '%"property": "stored"%'""")}
|
|
320
|
+
|
|
321
|
+
def rows(kind):
|
|
322
|
+
by: dict = {}
|
|
323
|
+
for r in st.q("""SELECT e.src, e.dst, e.kind, e.file, e.line, n.fqn FROM edges e JOIN nodes n ON n.id = e.src
|
|
324
|
+
WHERE e.kind = ? OR (e.kind = 'TEST_USES' AND e.attrs LIKE ?) ORDER BY e.dst, e.file, e.line""",
|
|
325
|
+
(kind, f'%"orig": "{kind}"%')):
|
|
326
|
+
if r["dst"] in stored and r["file"] and (include_tests or r["kind"] != "TEST_USES"):
|
|
327
|
+
d = dict(r)
|
|
328
|
+
d["test"] = r["kind"] == "TEST_USES"
|
|
329
|
+
by.setdefault(r["dst"], []).append(d)
|
|
330
|
+
return by
|
|
331
|
+
|
|
332
|
+
res = {"confidence": "heuristic", "lossy_sources": lsrc, "fields_with_writes": 0, "lossy_writes": 0, "findings": []}
|
|
333
|
+
writes = rows("WRITES_PROP")
|
|
334
|
+
res["fields_with_writes"] = len(writes)
|
|
335
|
+
lossy: dict = {}
|
|
336
|
+
for fid, ws in writes.items():
|
|
337
|
+
prop = re.split(r"[.$:#]", stored[fid])[-1]
|
|
338
|
+
sites = [_write_site(st, src, r, prop, globs) for r in ws]
|
|
339
|
+
if any(w["lossy"] for w in sites):
|
|
340
|
+
lossy[fid] = (prop, sites)
|
|
341
|
+
res["lossy_writes"] += sum(1 for w in sites if w["lossy"])
|
|
342
|
+
if lossy:
|
|
343
|
+
reads = rows("READS_PROP")
|
|
344
|
+
for fid, (prop, sites) in lossy.items():
|
|
345
|
+
for f in _pair(src, sites, [_read_site(st, src, r) for r in reads.get(fid, [])], prop):
|
|
346
|
+
res["findings"].append(dict(f, state=fid))
|
|
295
347
|
return res
|
|
296
348
|
|
|
297
349
|
|
|
350
|
+
def render_all(res: dict) -> str:
|
|
351
|
+
out = [f"roundtrip --all — heuristic; lossy names: {', '.join(res['lossy_sources'])}",
|
|
352
|
+
f"{res['fields_with_writes']} stored properties with writes, {res['lossy_writes']} lossy writes, "
|
|
353
|
+
f"{len(res['findings'])} round trips"]
|
|
354
|
+
for f in res["findings"]:
|
|
355
|
+
out.append(f" {f['state']}: write @{f['write']} -> {'/'.join(f['lossy']['calls'])} @{f['lossy']['at']} "
|
|
356
|
+
f"-> read @{f['read']} -> seeds {f['seeds']}")
|
|
357
|
+
for g in f["wider_ranges"]:
|
|
358
|
+
out.append(f" un-narrowed range {_fmt(g['range'])} @{g['at']}: {g['code']}")
|
|
359
|
+
return "\n".join(out)
|
|
360
|
+
|
|
361
|
+
|
|
298
362
|
def _read_back(src, w, x, prop) -> bool:
|
|
299
363
|
"""Is this read a read-back of the stored value rather than the same setup using what it just built? Not when
|
|
300
364
|
the writer reads it a few lines later (`scrollBar = ScrollBarView(...)` then `addView(scrollBar)` in one init),
|