codegraphcontext-rust 0.9.19__tar.gz → 0.9.21__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.
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/PKG-INFO +1 -1
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/pyproject.toml +1 -1
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/resolution/calls.rs +70 -14
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-python/src/lib.rs +21 -3
- codegraphcontext_rust-0.9.21/src/codegraphcontext/tools/indexing/angular_routes.py +396 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/cgc_wiki_cli.py +2 -2
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/engine.py +7 -3
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/persistence/duckdb_writer.py +76 -20
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/route_extraction.py +7 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/LICENSE +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/README.md +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/Cargo.lock +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/c_lang.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/cpp.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/csharp.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/dart.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/elixir.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/go.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/haskell.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/java.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/javascript.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/kotlin.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/mod.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/perl.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/php.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/python.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/ruby.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/rust_lang.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/scala.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/swift.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/ts_common.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/tsx.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lang/typescript.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/lib.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/parser.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/resolution/inheritance.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/resolution/mod.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/sanitize.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/types.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-python/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-python/src/conversions.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/build.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/alloc.h +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/array.h +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/parser.c +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/parser.h +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/scanner.c +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/grammar/tree_sitter/parser.h +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/tree-sitter-perl-vendor/src/lib.rs +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/cli_helpers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/config_manager.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/main.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/registry_commands.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/cli/visualizer.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/bundle_registry.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/cgc_bundle.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/cgcignore.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/database_falkordb.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/database_kuzu.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/core/watcher.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/server.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tool_definitions.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/code_finder.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/graph_builder.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/constants.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/context_generator.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/discovery.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/execution_flows.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/module_grouping.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/noise_filter.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/op_param_extraction.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/persistence/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/persistence/parquet_loader.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/persistence/writer.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/pipeline.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/pre_scan.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/rationale_extraction.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/resolution/__init__.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/resolution/calls.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/resolution/inheritance.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/sanitize.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/schema.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/schema_contract.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/indexing/scip_pipeline.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/c.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/cpp.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/dart.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/elixir.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/go.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/haskell.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/javascript.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/perl.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/python.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/ruby.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/rust.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/typescript.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/scip_indexer.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/scip_pb2.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/tools/tree_sitter_parser.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/utils/path_ignore.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/utils/repo_path.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/utils/visualize_graph.py +0 -0
- {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/src/codegraphcontext/viz/server.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codegraphcontext-rust"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.21"
|
|
8
8
|
description = "Rust-accelerated code indexing engine for CodeGraphContext. 19 languages, 5-18x faster parsing via tree-sitter + rayon."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Shashank Shekhar Singh", email = "shashankshekharsingh1205@gmail.com" },
|
{codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.21}/rust/cgc-core/src/resolution/calls.rs
RENAMED
|
@@ -351,6 +351,22 @@ pub struct CallGroups {
|
|
|
351
351
|
pub cls_to_cls: Vec<ResolvedCall>,
|
|
352
352
|
pub file_to_fn: Vec<ResolvedCall>,
|
|
353
353
|
pub file_to_cls: Vec<ResolvedCall>,
|
|
354
|
+
/// Calls that produced no edge, with the reason (Python builtins excluded).
|
|
355
|
+
pub unresolved: Vec<UnresolvedCall>,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// A call the resolver dropped, kept for diagnostics (upstream: `record_skip`).
|
|
359
|
+
#[derive(Debug, Clone)]
|
|
360
|
+
pub struct UnresolvedCall {
|
|
361
|
+
pub caller_file_path: String,
|
|
362
|
+
pub caller_name: Option<String>,
|
|
363
|
+
pub line_number: usize,
|
|
364
|
+
pub called_name: String,
|
|
365
|
+
pub full_call_name: String,
|
|
366
|
+
/// Receiver type the extractor inferred, if any
|
|
367
|
+
pub receiver_type: Option<String>,
|
|
368
|
+
/// external_receiver_type | external_chain_type | receiver_namesake | skip_external
|
|
369
|
+
pub reason: &'static str,
|
|
354
370
|
}
|
|
355
371
|
|
|
356
372
|
/// Locate the file declaring type `ty`. Returns (file, exact): exact when
|
|
@@ -375,8 +391,9 @@ fn locate_type(
|
|
|
375
391
|
Some((paths[0].clone(), false))
|
|
376
392
|
}
|
|
377
393
|
|
|
378
|
-
/// Resolve a single function call to its target
|
|
379
|
-
|
|
394
|
+
/// Resolve a single function call to its target; `Err(reason)` when the
|
|
395
|
+
/// call produces no edge.
|
|
396
|
+
pub fn resolve_call(
|
|
380
397
|
call: &CallInput,
|
|
381
398
|
caller_file_path: &str,
|
|
382
399
|
local_names: &HashSet<String>,
|
|
@@ -384,7 +401,7 @@ pub fn resolve_function_call(
|
|
|
384
401
|
imports_map: &HashMap<String, Vec<String>>,
|
|
385
402
|
types: &TypeIndex,
|
|
386
403
|
skip_external: bool,
|
|
387
|
-
) ->
|
|
404
|
+
) -> Result<ResolvedCall, &'static str> {
|
|
388
405
|
let called_name = &call.name;
|
|
389
406
|
let full_call = &call.full_name;
|
|
390
407
|
|
|
@@ -393,7 +410,7 @@ pub fn resolve_function_call(
|
|
|
393
410
|
// from another language — is a regular method call.
|
|
394
411
|
let is_python = caller_file_path.ends_with(".py") || caller_file_path.ends_with(".pyi");
|
|
395
412
|
if is_python && !full_call.contains('.') && PYTHON_BUILTINS.contains(&called_name.as_str()) {
|
|
396
|
-
return
|
|
413
|
+
return Err("python_builtin");
|
|
397
414
|
}
|
|
398
415
|
|
|
399
416
|
let base_obj = if full_call.contains('.') {
|
|
@@ -514,7 +531,7 @@ pub fn resolve_function_call(
|
|
|
514
531
|
resolved = Some((found.map(|(_, f, _)| f).unwrap_or(file), 4));
|
|
515
532
|
}
|
|
516
533
|
// e.g. repo.findAll().stream(): target is outside the graph
|
|
517
|
-
ChainType::External => return
|
|
534
|
+
ChainType::External => return Err("external_chain_type"),
|
|
518
535
|
ChainType::Unknown => {}
|
|
519
536
|
}
|
|
520
537
|
}
|
|
@@ -581,7 +598,7 @@ pub fn resolve_function_call(
|
|
|
581
598
|
// The receiver's type is known but lives outside the repo: the
|
|
582
599
|
// target can't be in the graph (upstream: receiver_resolution_failed).
|
|
583
600
|
if external_receiver_type {
|
|
584
|
-
return
|
|
601
|
+
return Err("external_receiver_type");
|
|
585
602
|
}
|
|
586
603
|
// Pointing it at the caller's file would bind it to a same-named
|
|
587
604
|
// local function (false recursion), so drop it in that case —
|
|
@@ -589,7 +606,7 @@ pub fn resolve_function_call(
|
|
|
589
606
|
if local_names.contains(called_name.as_str())
|
|
590
607
|
|| call.context_name.as_deref() == Some(called_name.as_str())
|
|
591
608
|
{
|
|
592
|
-
return
|
|
609
|
+
return Err("receiver_namesake");
|
|
593
610
|
}
|
|
594
611
|
resolved = Some((own(), 9));
|
|
595
612
|
} else if !qualified_other && local_names.contains(called_name.as_str()) {
|
|
@@ -606,7 +623,7 @@ pub fn resolve_function_call(
|
|
|
606
623
|
})
|
|
607
624
|
.collect();
|
|
608
625
|
if candidates.is_empty() && qualified_other && local_names.contains(called_name.as_str()) {
|
|
609
|
-
return
|
|
626
|
+
return Err("receiver_namesake");
|
|
610
627
|
}
|
|
611
628
|
// Try matching via local imports
|
|
612
629
|
for p in &candidates {
|
|
@@ -628,7 +645,7 @@ pub fn resolve_function_call(
|
|
|
628
645
|
}
|
|
629
646
|
|
|
630
647
|
if skip_external && is_unresolved_external {
|
|
631
|
-
return
|
|
648
|
+
return Err("skip_external");
|
|
632
649
|
}
|
|
633
650
|
|
|
634
651
|
let (resolved_path, tier) = resolved.unwrap_or_else(|| (own(), 9));
|
|
@@ -638,7 +655,7 @@ pub fn resolve_function_call(
|
|
|
638
655
|
if let (Some(name), Some(_), Some(line)) =
|
|
639
656
|
(&call.context_name, &call.context_type, call.context_line)
|
|
640
657
|
{
|
|
641
|
-
|
|
658
|
+
Ok(ResolvedCall {
|
|
642
659
|
call_type: "function".to_string(),
|
|
643
660
|
caller_name: Some(name.clone()),
|
|
644
661
|
caller_file_path: caller_file_path.to_string(),
|
|
@@ -654,7 +671,7 @@ pub fn resolve_function_call(
|
|
|
654
671
|
dispatch_class: dispatch_class.clone(),
|
|
655
672
|
})
|
|
656
673
|
} else {
|
|
657
|
-
|
|
674
|
+
Ok(ResolvedCall {
|
|
658
675
|
call_type: "file".to_string(),
|
|
659
676
|
caller_name: None,
|
|
660
677
|
caller_file_path: caller_file_path.to_string(),
|
|
@@ -672,6 +689,19 @@ pub fn resolve_function_call(
|
|
|
672
689
|
}
|
|
673
690
|
}
|
|
674
691
|
|
|
692
|
+
/// Resolve a single function call to its target (None when dropped).
|
|
693
|
+
pub fn resolve_function_call(
|
|
694
|
+
call: &CallInput,
|
|
695
|
+
caller_file_path: &str,
|
|
696
|
+
local_names: &HashSet<String>,
|
|
697
|
+
local_imports: &HashMap<String, String>,
|
|
698
|
+
imports_map: &HashMap<String, Vec<String>>,
|
|
699
|
+
types: &TypeIndex,
|
|
700
|
+
skip_external: bool,
|
|
701
|
+
) -> Option<ResolvedCall> {
|
|
702
|
+
resolve_call(call, caller_file_path, local_names, local_imports, imports_map, types, skip_external).ok()
|
|
703
|
+
}
|
|
704
|
+
|
|
675
705
|
/// Language extension map for filtering imports_map by language.
|
|
676
706
|
fn lang_extensions(lang: &str) -> Option<&[&str]> {
|
|
677
707
|
match lang {
|
|
@@ -763,7 +793,7 @@ pub fn build_function_call_groups(
|
|
|
763
793
|
};
|
|
764
794
|
|
|
765
795
|
for call in &file_data.calls {
|
|
766
|
-
let first = match
|
|
796
|
+
let first = match resolve_call(
|
|
767
797
|
call,
|
|
768
798
|
caller_file_path,
|
|
769
799
|
&local_names_owned,
|
|
@@ -772,8 +802,20 @@ pub fn build_function_call_groups(
|
|
|
772
802
|
&types,
|
|
773
803
|
skip_external,
|
|
774
804
|
) {
|
|
775
|
-
|
|
776
|
-
|
|
805
|
+
Ok(r) => r,
|
|
806
|
+
Err("python_builtin") => continue,
|
|
807
|
+
Err(reason) => {
|
|
808
|
+
groups.unresolved.push(UnresolvedCall {
|
|
809
|
+
caller_file_path: caller_file_path.to_string(),
|
|
810
|
+
caller_name: call.context_name.clone(),
|
|
811
|
+
line_number: call.line_number,
|
|
812
|
+
called_name: call.name.clone(),
|
|
813
|
+
full_call_name: call.full_name.clone(),
|
|
814
|
+
receiver_type: call.inferred_obj_type.clone(),
|
|
815
|
+
reason,
|
|
816
|
+
});
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
777
819
|
};
|
|
778
820
|
|
|
779
821
|
// Virtual dispatch (upstream: Java interface fan-out): a call
|
|
@@ -1313,4 +1355,18 @@ mod tests {
|
|
|
1313
1355
|
let r = resolve_function_call(&call, "/r/Log.ts", &HashSet::new(), &HashMap::new(), &HashMap::new(), &TypeIndex::default(), false);
|
|
1314
1356
|
assert!(r.is_none());
|
|
1315
1357
|
}
|
|
1358
|
+
|
|
1359
|
+
#[test]
|
|
1360
|
+
fn test_unresolved_calls_are_recorded_with_reason() {
|
|
1361
|
+
let mut f = file("/r/A.java", &[("A", &["run", "add"], &[])], vec![]);
|
|
1362
|
+
let mut ext = receiver_call("add", "items.add", Some("List"));
|
|
1363
|
+
ext.context_name = Some("run".into());
|
|
1364
|
+
let mut builtin = receiver_call("len", "len", None);
|
|
1365
|
+
builtin.context_name = Some("run".into());
|
|
1366
|
+
f.calls = vec![ext, builtin];
|
|
1367
|
+
let groups = build_function_call_groups(&[f], &HashMap::new(), &HashMap::new(), false);
|
|
1368
|
+
assert_eq!(groups.unresolved.len(), 1); // the builtin isn't recorded (and A.java isn't Python anyway)
|
|
1369
|
+
let u = &groups.unresolved[0];
|
|
1370
|
+
assert_eq!((u.called_name.as_str(), u.reason, u.receiver_type.as_deref()), ("add", "external_receiver_type", Some("List")));
|
|
1371
|
+
}
|
|
1316
1372
|
}
|
|
@@ -88,12 +88,13 @@ fn parse_and_prescan(
|
|
|
88
88
|
|
|
89
89
|
/// Convert Python all_file_data + imports_map into Rust types, run resolution, return results.
|
|
90
90
|
#[pyfunction]
|
|
91
|
-
#[pyo3(signature = (all_file_data, imports_map, skip_external=false))]
|
|
91
|
+
#[pyo3(signature = (all_file_data, imports_map, skip_external=false, with_unresolved=false))]
|
|
92
92
|
fn resolve_call_groups(
|
|
93
93
|
py: Python<'_>,
|
|
94
94
|
all_file_data: &Bound<'_, PyList>,
|
|
95
95
|
imports_map: &Bound<'_, PyDict>,
|
|
96
96
|
skip_external: bool,
|
|
97
|
+
with_unresolved: bool,
|
|
97
98
|
) -> PyResult<PyObject> {
|
|
98
99
|
// Convert imports_map: Python dict -> Rust HashMap
|
|
99
100
|
let rust_imports_map = py_dict_to_imports_map(imports_map)?;
|
|
@@ -120,8 +121,25 @@ fn resolve_call_groups(
|
|
|
120
121
|
build_function_call_groups(&rust_files, &rust_imports_map, &file_class_lookup, skip_external)
|
|
121
122
|
});
|
|
122
123
|
|
|
123
|
-
// Convert back to Python: 6-tuple of lists of dicts
|
|
124
|
-
|
|
124
|
+
// Convert back to Python: 6-tuple of lists of dicts; with_unresolved
|
|
125
|
+
// returns (6-tuple, [unresolved call dicts]) instead.
|
|
126
|
+
let call_groups = call_groups_to_py(py, &groups)?;
|
|
127
|
+
if !with_unresolved {
|
|
128
|
+
return Ok(call_groups);
|
|
129
|
+
}
|
|
130
|
+
let unresolved = PyList::empty(py);
|
|
131
|
+
for u in &groups.unresolved {
|
|
132
|
+
let d = PyDict::new(py);
|
|
133
|
+
d.set_item("caller_file_path", &u.caller_file_path)?;
|
|
134
|
+
d.set_item("caller_name", &u.caller_name)?;
|
|
135
|
+
d.set_item("line_number", u.line_number)?;
|
|
136
|
+
d.set_item("called_name", &u.called_name)?;
|
|
137
|
+
d.set_item("full_call_name", &u.full_call_name)?;
|
|
138
|
+
d.set_item("receiver_type", &u.receiver_type)?;
|
|
139
|
+
d.set_item("reason", u.reason)?;
|
|
140
|
+
unresolved.append(d)?;
|
|
141
|
+
}
|
|
142
|
+
Ok(PyTuple::new(py, [call_groups, unresolved.into_any().unbind()])?.into_any().unbind())
|
|
125
143
|
}
|
|
126
144
|
|
|
127
145
|
/// Resolve inheritance links.
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""Angular Router: route tables, lazy-loaded modules and navigation calls.
|
|
2
|
+
|
|
3
|
+
Routes come from `Routes` arrays passed to `RouterModule.forRoot/forChild`
|
|
4
|
+
or `provideRouter`, or exported for `loadChildren`. Full paths are built by
|
|
5
|
+
following `loadChildren: () => import('./x/x.module').then(m => m.XModule)`
|
|
6
|
+
from the root routes into the child module's routing file, so a page gets
|
|
7
|
+
`/home/tabs/tab1` rather than just `tab1`.
|
|
8
|
+
|
|
9
|
+
Navigations are `router.navigate([...])` / `router.navigateByUrl('...')`
|
|
10
|
+
calls, attributed to their enclosing function and matched to a route.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
_ROUTE_ARRAY_DECL = re.compile(
|
|
20
|
+
r"\b(?:export\s+)?(?:const|let|var)\s+(\w+)\s*(?::\s*(Routes|Route\s*\[\s*\]))?\s*=\s*\["
|
|
21
|
+
)
|
|
22
|
+
_ROUTER_CALL = re.compile(r"\b(?:RouterModule\.(forRoot|forChild)|(provideRouter))\s*\(")
|
|
23
|
+
_IMPORT_SPEC = re.compile(r"import\(\s*['\"]([^'\"]+)['\"]\s*\)")
|
|
24
|
+
_THEN_SYMBOL = re.compile(r"\.then\(\s*\(?\s*(\w+)\s*\)?\s*=>\s*\1\.(\w+)")
|
|
25
|
+
_NAVIGATE = re.compile(r"\b(?:\w+\.)*\w*[Rr]outer\s*\.\s*(navigate|navigateByUrl)\s*\(")
|
|
26
|
+
_TS_EXTS = (".ts", ".tsx", ".js", ".mjs")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ── Small JS literal parser ──────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
def _strip_comments(src: str) -> str:
|
|
32
|
+
"""Blank out // and /* */ comments (string-aware, keeps offsets)."""
|
|
33
|
+
out, i, n = [], 0, len(src)
|
|
34
|
+
while i < n:
|
|
35
|
+
c = src[i]
|
|
36
|
+
if c in "'\"`":
|
|
37
|
+
j = i + 1
|
|
38
|
+
while j < n and src[j] != c:
|
|
39
|
+
j += 2 if src[j] == "\\" else 1
|
|
40
|
+
out.append(src[i:j + 1]); i = j + 1
|
|
41
|
+
elif src.startswith("//", i):
|
|
42
|
+
j = src.find("\n", i); j = n if j == -1 else j
|
|
43
|
+
out.append(" " * (j - i)); i = j
|
|
44
|
+
elif src.startswith("/*", i):
|
|
45
|
+
j = src.find("*/", i + 2); j = n if j == -1 else j + 2
|
|
46
|
+
out.append(re.sub(r"[^\n]", " ", src[i:j])); i = j
|
|
47
|
+
else:
|
|
48
|
+
out.append(c); i += 1
|
|
49
|
+
return "".join(out)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _match_close(code: str, i: int) -> int:
|
|
53
|
+
"""Index just past the bracket matching code[i] ('[', '{' or '(')."""
|
|
54
|
+
depth, n = 0, len(code)
|
|
55
|
+
while i < n:
|
|
56
|
+
c = code[i]
|
|
57
|
+
if c in "'\"`":
|
|
58
|
+
j = i + 1
|
|
59
|
+
while j < n and code[j] != c:
|
|
60
|
+
j += 2 if code[j] == "\\" else 1
|
|
61
|
+
i = j + 1
|
|
62
|
+
continue
|
|
63
|
+
if c in "[{(":
|
|
64
|
+
depth += 1
|
|
65
|
+
elif c in "]})":
|
|
66
|
+
depth -= 1
|
|
67
|
+
if depth == 0:
|
|
68
|
+
return i + 1
|
|
69
|
+
i += 1
|
|
70
|
+
return n
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _split_top(code: str, start: int, end: int) -> List[Tuple[int, int]]:
|
|
74
|
+
"""Comma-separated top-level segments of code[start:end]."""
|
|
75
|
+
segs, depth, seg_start, i = [], 0, start, start
|
|
76
|
+
while i < end:
|
|
77
|
+
c = code[i]
|
|
78
|
+
if c in "'\"`":
|
|
79
|
+
j = i + 1
|
|
80
|
+
while j < end and code[j] != c:
|
|
81
|
+
j += 2 if code[j] == "\\" else 1
|
|
82
|
+
i = j + 1
|
|
83
|
+
continue
|
|
84
|
+
if c in "[{(":
|
|
85
|
+
depth += 1
|
|
86
|
+
elif c in "]})":
|
|
87
|
+
depth -= 1
|
|
88
|
+
elif c == "," and depth == 0:
|
|
89
|
+
segs.append((seg_start, i)); seg_start = i + 1
|
|
90
|
+
i += 1
|
|
91
|
+
segs.append((seg_start, end))
|
|
92
|
+
return [(a, b) for a, b in segs if code[a:b].strip()]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _string_value(text: str) -> Optional[str]:
|
|
96
|
+
m = re.fullmatch(r"\s*(['\"`])(.*?)\1\s*", text, re.S)
|
|
97
|
+
return m.group(2) if m else None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _parse_object(code: str, start: int, end: int) -> Dict[str, Tuple[str, int]]:
|
|
101
|
+
"""Top-level `key: value` pairs of an object literal code[start:end] ({...})."""
|
|
102
|
+
props = {}
|
|
103
|
+
for a, b in _split_top(code, start + 1, end - 1):
|
|
104
|
+
m = re.match(r"\s*['\"]?(\w+)['\"]?\s*:", code[a:b])
|
|
105
|
+
if m:
|
|
106
|
+
vstart = a + m.end()
|
|
107
|
+
props[m.group(1)] = (code[vstart:b].strip(), vstart + (len(code[vstart:b]) - len(code[vstart:b].lstrip())))
|
|
108
|
+
return props
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _parse_routes(code: str, start: int, end: int) -> List[Dict[str, Any]]:
|
|
112
|
+
"""Parse a routes array code[start:end] ([...]) into route dicts."""
|
|
113
|
+
routes = []
|
|
114
|
+
for a, b in _split_top(code, start + 1, end - 1):
|
|
115
|
+
seg = code[a:b].strip()
|
|
116
|
+
off = a + (len(code[a:b]) - len(code[a:b].lstrip()))
|
|
117
|
+
if seg.startswith("..."): # spread of another routes array
|
|
118
|
+
routes.append({"ref": seg[3:].strip(), "pos": off})
|
|
119
|
+
continue
|
|
120
|
+
if re.fullmatch(r"\w+", seg):
|
|
121
|
+
routes.append({"ref": seg, "pos": off})
|
|
122
|
+
continue
|
|
123
|
+
if not seg.startswith("{"):
|
|
124
|
+
continue
|
|
125
|
+
props = _parse_object(code, off, _match_close(code, off))
|
|
126
|
+
route: Dict[str, Any] = {"pos": off, "path": None}
|
|
127
|
+
if "path" in props:
|
|
128
|
+
route["path"] = _string_value(props["path"][0])
|
|
129
|
+
if "component" in props:
|
|
130
|
+
route["component"] = props["component"][0].strip()
|
|
131
|
+
if "redirectTo" in props:
|
|
132
|
+
route["redirect"] = _string_value(props["redirectTo"][0]) or props["redirectTo"][0]
|
|
133
|
+
for key in ("loadChildren", "loadComponent"):
|
|
134
|
+
if key in props:
|
|
135
|
+
val = props[key][0]
|
|
136
|
+
spec = _IMPORT_SPEC.search(val)
|
|
137
|
+
sym = _THEN_SYMBOL.search(val)
|
|
138
|
+
legacy = _string_value(val) # 'path/to/x.module#XModule'
|
|
139
|
+
if spec:
|
|
140
|
+
route[key] = (spec.group(1), sym.group(2) if sym else None)
|
|
141
|
+
elif legacy and "#" in legacy:
|
|
142
|
+
p, s = legacy.split("#", 1)
|
|
143
|
+
route[key] = (p, s)
|
|
144
|
+
if "children" in props and props["children"][0].startswith("["):
|
|
145
|
+
cstart = props["children"][1]
|
|
146
|
+
route["children"] = _parse_routes(code, cstart, _match_close(code, cstart))
|
|
147
|
+
routes.append(route)
|
|
148
|
+
return routes
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ── Per-file route tables ────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
def _line_of(code: str, pos: int) -> int:
|
|
154
|
+
return code.count("\n", 0, pos) + 1
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _file_routes(code: str) -> Dict[str, Any]:
|
|
158
|
+
"""Named route arrays and router registrations found in one file."""
|
|
159
|
+
arrays: Dict[str, Dict[str, Any]] = {}
|
|
160
|
+
for m in _ROUTE_ARRAY_DECL.finditer(code):
|
|
161
|
+
start = m.end() - 1
|
|
162
|
+
arrays[m.group(1)] = {
|
|
163
|
+
"routes": _parse_routes(code, start, _match_close(code, start)),
|
|
164
|
+
"typed": bool(m.group(2)),
|
|
165
|
+
}
|
|
166
|
+
registrations = [] # (mode, array name or None, inline routes or None)
|
|
167
|
+
for m in _ROUTER_CALL.finditer(code):
|
|
168
|
+
mode = "root" if (m.group(1) == "forRoot" or m.group(2)) else "child"
|
|
169
|
+
args_start = m.end()
|
|
170
|
+
while args_start < len(code) and code[args_start].isspace():
|
|
171
|
+
args_start += 1
|
|
172
|
+
if args_start < len(code) and code[args_start] == "[":
|
|
173
|
+
registrations.append((mode, None, _parse_routes(code, args_start, _match_close(code, args_start))))
|
|
174
|
+
else:
|
|
175
|
+
ident = re.match(r"(\w+)", code[args_start:])
|
|
176
|
+
if ident:
|
|
177
|
+
registrations.append((mode, ident.group(1), None))
|
|
178
|
+
return {"arrays": arrays, "registrations": registrations}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _resolve_import(from_rel: str, spec: str, known: set) -> Optional[str]:
|
|
182
|
+
if not spec.startswith("."):
|
|
183
|
+
return None
|
|
184
|
+
base = os.path.normpath(os.path.join(os.path.dirname(from_rel), spec))
|
|
185
|
+
for cand in [base] + [base + e for e in _TS_EXTS] + [os.path.join(base, "index" + e) for e in _TS_EXTS]:
|
|
186
|
+
if cand in known:
|
|
187
|
+
return cand
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _join(prefix: str, path: Optional[str]) -> str:
|
|
192
|
+
# `path: \`${PREFIX}/x\`` → dynamic segment
|
|
193
|
+
path = re.sub(r"\$\{[^}]*\}", ":param", path or "")
|
|
194
|
+
parts = [p for p in (prefix.strip("/"), path.strip("/")) if p]
|
|
195
|
+
return "/" + "/".join(parts)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def extract_angular_routes(
|
|
199
|
+
parsed_results: List[Dict[str, Any]],
|
|
200
|
+
repo_path: str,
|
|
201
|
+
) -> List[Dict[str, Any]]:
|
|
202
|
+
"""Angular routes with full paths, as route dicts (method PAGE/REDIRECT)."""
|
|
203
|
+
files: Dict[str, Dict[str, Any]] = {}
|
|
204
|
+
codes: Dict[str, str] = {}
|
|
205
|
+
for fd in parsed_results:
|
|
206
|
+
path = fd.get("path", "")
|
|
207
|
+
if fd.get("lang") not in ("typescript", "tsx", "javascript") or not os.path.exists(path):
|
|
208
|
+
continue
|
|
209
|
+
try:
|
|
210
|
+
src = open(path, encoding="utf-8", errors="replace").read()
|
|
211
|
+
except OSError:
|
|
212
|
+
continue
|
|
213
|
+
if "Route" not in src and "provideRouter" not in src:
|
|
214
|
+
continue
|
|
215
|
+
rel = os.path.relpath(path, repo_path)
|
|
216
|
+
code = _strip_comments(src)
|
|
217
|
+
info = _file_routes(code)
|
|
218
|
+
if info["arrays"] or info["registrations"]:
|
|
219
|
+
files[rel] = info
|
|
220
|
+
codes[rel] = code
|
|
221
|
+
if not files:
|
|
222
|
+
return []
|
|
223
|
+
known = {os.path.relpath(fd.get("path", ""), repo_path) for fd in parsed_results}
|
|
224
|
+
|
|
225
|
+
def array_by_name(rel: str, name: str):
|
|
226
|
+
arr = files.get(rel, {}).get("arrays", {}).get(name)
|
|
227
|
+
return arr["routes"] if arr else None
|
|
228
|
+
|
|
229
|
+
def child_tables(target: str, symbol: Optional[str]) -> List[Tuple[str, List]]:
|
|
230
|
+
"""Route tables a lazy `loadChildren` target contributes."""
|
|
231
|
+
out = []
|
|
232
|
+
if target in files:
|
|
233
|
+
if symbol and array_by_name(target, symbol) is not None:
|
|
234
|
+
return [(target, array_by_name(target, symbol))]
|
|
235
|
+
out += [(target, routes) for _, routes in _registered(target)]
|
|
236
|
+
if not out: # the module imports a sibling *-routing.module
|
|
237
|
+
d = os.path.dirname(target)
|
|
238
|
+
for rel in files:
|
|
239
|
+
if os.path.dirname(rel) == d and rel != target and ("routing" in rel or "routes" in rel):
|
|
240
|
+
out += [(rel, routes) for mode, routes in _registered(rel) if mode == "child"]
|
|
241
|
+
return out
|
|
242
|
+
|
|
243
|
+
def _registered(rel: str) -> List[Tuple[str, List]]:
|
|
244
|
+
info = files[rel]
|
|
245
|
+
out = []
|
|
246
|
+
for mode, name, inline in info["registrations"]:
|
|
247
|
+
routes = inline if inline is not None else array_by_name(rel, name) if name else None
|
|
248
|
+
if routes is not None:
|
|
249
|
+
out.append((mode, routes))
|
|
250
|
+
return out
|
|
251
|
+
|
|
252
|
+
results: List[Dict[str, Any]] = []
|
|
253
|
+
emitted = set()
|
|
254
|
+
reached = set() # ids of route lists visited from a root
|
|
255
|
+
|
|
256
|
+
def walk(rel: str, routes: List, prefix: str, depth: int):
|
|
257
|
+
if depth > 12:
|
|
258
|
+
return
|
|
259
|
+
reached.add(id(routes))
|
|
260
|
+
for r in routes:
|
|
261
|
+
if "ref" in r: # ...OTHER_ROUTES / OTHER_ROUTES inside the array
|
|
262
|
+
ref = array_by_name(rel, r["ref"])
|
|
263
|
+
if ref is not None and id(ref) not in reached:
|
|
264
|
+
walk(rel, ref, prefix, depth + 1)
|
|
265
|
+
continue
|
|
266
|
+
full = _join(prefix, r.get("path"))
|
|
267
|
+
line = _line_of(codes[rel], r["pos"])
|
|
268
|
+
handler, method = None, "PAGE"
|
|
269
|
+
if r.get("component"):
|
|
270
|
+
handler = r["component"]
|
|
271
|
+
elif r.get("loadComponent"):
|
|
272
|
+
handler = r["loadComponent"][1] or r["loadComponent"][0]
|
|
273
|
+
elif r.get("loadChildren"):
|
|
274
|
+
handler = r["loadChildren"][1] or r["loadChildren"][0]
|
|
275
|
+
elif r.get("redirect") is not None:
|
|
276
|
+
to = str(r["redirect"])
|
|
277
|
+
handler = "→ " + (to if to.startswith("/") else _join(prefix, to))
|
|
278
|
+
method = "REDIRECT"
|
|
279
|
+
if handler and (method, full, rel, line) not in emitted:
|
|
280
|
+
emitted.add((method, full, rel, line))
|
|
281
|
+
results.append({"method": method, "path": full, "handler": handler,
|
|
282
|
+
"file": rel, "line": line, "framework": "angular"})
|
|
283
|
+
if r.get("children"):
|
|
284
|
+
walk(rel, r["children"], full, depth + 1)
|
|
285
|
+
if r.get("loadChildren"):
|
|
286
|
+
spec, symbol = r["loadChildren"]
|
|
287
|
+
target = _resolve_import(rel, spec, known)
|
|
288
|
+
if target:
|
|
289
|
+
for trel, troutes in child_tables(target, symbol):
|
|
290
|
+
if id(troutes) not in reached:
|
|
291
|
+
walk(trel, troutes, full, depth + 1)
|
|
292
|
+
|
|
293
|
+
for rel in files:
|
|
294
|
+
for mode, routes in _registered(rel):
|
|
295
|
+
if mode == "root":
|
|
296
|
+
walk(rel, routes, "", 0)
|
|
297
|
+
# Child tables never reached from a root (unknown mount point): keep
|
|
298
|
+
# their local paths so the pages are still listed.
|
|
299
|
+
for rel in files:
|
|
300
|
+
for mode, routes in _registered(rel):
|
|
301
|
+
if id(routes) not in reached:
|
|
302
|
+
walk(rel, routes, "", 0)
|
|
303
|
+
return results
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ── Navigations ──────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
def _nav_target(code: str, args_start: int, kind: str) -> Optional[str]:
|
|
309
|
+
"""Target path of a navigate([...]) / navigateByUrl('...') call.
|
|
310
|
+
|
|
311
|
+
Non-literal segments become `:param`; a path is absolute when its first
|
|
312
|
+
literal segment starts with '/'.
|
|
313
|
+
"""
|
|
314
|
+
while args_start < len(code) and code[args_start].isspace():
|
|
315
|
+
args_start += 1
|
|
316
|
+
if kind == "navigate":
|
|
317
|
+
if args_start >= len(code) or code[args_start] != "[":
|
|
318
|
+
return None
|
|
319
|
+
end = _match_close(code, args_start)
|
|
320
|
+
parts, absolute = [], False
|
|
321
|
+
for idx, (a, b) in enumerate(_split_top(code, args_start + 1, end - 1)):
|
|
322
|
+
s = _string_value(code[a:b])
|
|
323
|
+
if s is None:
|
|
324
|
+
parts.append(":param")
|
|
325
|
+
continue
|
|
326
|
+
if idx == 0 and s.startswith("/"):
|
|
327
|
+
absolute = True
|
|
328
|
+
parts += [":param" if "${" in p else p for p in s.split("/") if p]
|
|
329
|
+
return ("/" if absolute else "") + "/".join(parts)
|
|
330
|
+
m = re.match(r"(['\"`])(.*?)\1", code[args_start:args_start + 500], re.S)
|
|
331
|
+
if not m:
|
|
332
|
+
return None
|
|
333
|
+
return re.sub(r"\$\{[^}]*\}", ":param", m.group(2).split("?")[0])
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _match_route(target: str, routes: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
337
|
+
"""Best route for an absolute target path (literal segments must match)."""
|
|
338
|
+
if not target.startswith("/"):
|
|
339
|
+
return None
|
|
340
|
+
tseg = [s for s in target.strip("/").split("/") if s]
|
|
341
|
+
best, best_score = None, -1
|
|
342
|
+
for r in routes:
|
|
343
|
+
if r["method"] != "PAGE":
|
|
344
|
+
continue
|
|
345
|
+
rseg = [s for s in r["path"].strip("/").split("/") if s]
|
|
346
|
+
if len(rseg) != len(tseg):
|
|
347
|
+
continue
|
|
348
|
+
score = 0
|
|
349
|
+
for a, b in zip(rseg, tseg):
|
|
350
|
+
if a.startswith(":") or b == ":param":
|
|
351
|
+
continue
|
|
352
|
+
if a != b:
|
|
353
|
+
break
|
|
354
|
+
score += 1
|
|
355
|
+
else:
|
|
356
|
+
if score > best_score:
|
|
357
|
+
best, best_score = r, score
|
|
358
|
+
return best
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def extract_navigations(
|
|
362
|
+
parsed_results: List[Dict[str, Any]],
|
|
363
|
+
repo_path: str,
|
|
364
|
+
angular_routes: List[Dict[str, Any]],
|
|
365
|
+
) -> List[Dict[str, Any]]:
|
|
366
|
+
"""router.navigate / navigateByUrl calls with their enclosing function and route."""
|
|
367
|
+
out = []
|
|
368
|
+
for fd in parsed_results:
|
|
369
|
+
path = fd.get("path", "")
|
|
370
|
+
if fd.get("lang") not in ("typescript", "tsx", "javascript") or not os.path.exists(path):
|
|
371
|
+
continue
|
|
372
|
+
try:
|
|
373
|
+
src = open(path, encoding="utf-8", errors="replace").read()
|
|
374
|
+
except OSError:
|
|
375
|
+
continue
|
|
376
|
+
if "navigate" not in src:
|
|
377
|
+
continue
|
|
378
|
+
code = _strip_comments(src)
|
|
379
|
+
rel = os.path.relpath(path, repo_path)
|
|
380
|
+
funcs = [(f.get("line_number", 0), f.get("end_line", 0) or f.get("line_number", 0), f.get("name", ""))
|
|
381
|
+
for f in fd.get("functions", [])]
|
|
382
|
+
for m in _NAVIGATE.finditer(code):
|
|
383
|
+
target = _nav_target(code, m.end(), m.group(1))
|
|
384
|
+
if target is None:
|
|
385
|
+
continue
|
|
386
|
+
line = _line_of(code, m.start())
|
|
387
|
+
enclosing = [f for f in funcs if f[0] <= line <= f[1]]
|
|
388
|
+
caller = max(enclosing, key=lambda f: f[0])[2] if enclosing else ""
|
|
389
|
+
route = _match_route(target, angular_routes)
|
|
390
|
+
out.append({
|
|
391
|
+
"caller_name": caller, "caller_path": rel, "line": line, "target": target,
|
|
392
|
+
"route_path": route["path"] if route else "",
|
|
393
|
+
"route_handler": route["handler"] if route else "",
|
|
394
|
+
"route_file": route["file"] if route else "",
|
|
395
|
+
})
|
|
396
|
+
return out
|
|
@@ -75,7 +75,7 @@ def cmd_index(args):
|
|
|
75
75
|
specs = [(f, l, False) for f, l in files]
|
|
76
76
|
results, imports_map = parse_and_prescan(specs)
|
|
77
77
|
valid = [r for r in results if "error" not in r]
|
|
78
|
-
call_groups = resolve_call_groups(valid, imports_map, False)
|
|
78
|
+
call_groups, unresolved = resolve_call_groups(valid, imports_map, False, True)
|
|
79
79
|
inheritance, _ = resolve_inheritance(valid, imports_map)
|
|
80
80
|
t_parse = time.time() - t0
|
|
81
81
|
print(f"\nParse + resolve: {t_parse:.1f}s")
|
|
@@ -86,7 +86,7 @@ def cmd_index(args):
|
|
|
86
86
|
db_path = os.path.join(output_dir, "graph.duckdb")
|
|
87
87
|
t0 = time.time()
|
|
88
88
|
writer = DuckDBGraphWriter(db_path)
|
|
89
|
-
counts = writer.write_all(valid, repo_path, call_groups, inheritance)
|
|
89
|
+
counts = writer.write_all(valid, repo_path, call_groups, inheritance, unresolved=unresolved)
|
|
90
90
|
t_write = time.time() - t0
|
|
91
91
|
|
|
92
92
|
# Read back from DB (no duplicate extraction)
|
|
@@ -81,11 +81,15 @@ def pre_scan_for_imports(file_specs):
|
|
|
81
81
|
return _rust_pre_scan(file_specs)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
def resolve_call_groups(all_file_data, imports_map, skip_external=False):
|
|
85
|
-
"""Resolve function calls into 6-category groups using Rust.
|
|
84
|
+
def resolve_call_groups(all_file_data, imports_map, skip_external=False, with_unresolved=False):
|
|
85
|
+
"""Resolve function calls into 6-category groups using Rust.
|
|
86
|
+
|
|
87
|
+
With ``with_unresolved=True`` returns ``(groups, unresolved)`` where
|
|
88
|
+
``unresolved`` lists the calls that produced no edge and why.
|
|
89
|
+
"""
|
|
86
90
|
if not RUST_AVAILABLE:
|
|
87
91
|
raise RuntimeError("Rust engine required for resolve_call_groups")
|
|
88
|
-
return _rust_resolve_calls(all_file_data, imports_map, skip_external)
|
|
92
|
+
return _rust_resolve_calls(all_file_data, imports_map, skip_external, with_unresolved)
|
|
89
93
|
|
|
90
94
|
|
|
91
95
|
def resolve_inheritance(all_file_data, imports_map):
|