codegraphcontext-rust 0.9.14__tar.gz → 0.9.15__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.14 → codegraphcontext_rust-0.9.15}/PKG-INFO +1 -1
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/pyproject.toml +1 -1
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/java.rs +187 -15
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/resolution/calls.rs +105 -5
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/route_extraction.py +170 -73
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/LICENSE +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/README.md +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/Cargo.lock +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/c_lang.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/cpp.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/csharp.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/dart.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/elixir.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/go.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/haskell.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/javascript.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/kotlin.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/mod.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/perl.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/php.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/python.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/ruby.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/rust_lang.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/scala.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/swift.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/tsx.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/typescript.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lib.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/parser.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/resolution/inheritance.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/resolution/mod.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/sanitize.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/types.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-python/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-python/src/conversions.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-python/src/lib.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/Cargo.toml +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/build.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/alloc.h +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/array.h +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/parser.c +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/parser.h +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/scanner.c +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/grammar/tree_sitter/parser.h +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/tree-sitter-perl-vendor/src/lib.rs +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/cli_helpers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/config_manager.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/main.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/registry_commands.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/cli/visualizer.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/bundle_registry.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/cgc_bundle.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/cgcignore.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/database_falkordb.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/database_kuzu.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/core/watcher.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/server.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tool_definitions.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/code_finder.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/graph_builder.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/cgc_wiki_cli.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/constants.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/context_generator.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/discovery.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/engine.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/execution_flows.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/module_grouping.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/noise_filter.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/op_param_extraction.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/persistence/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/persistence/duckdb_writer.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/persistence/parquet_loader.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/persistence/writer.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/pipeline.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/pre_scan.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/rationale_extraction.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/resolution/__init__.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/resolution/calls.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/resolution/inheritance.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/sanitize.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/schema.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/schema_contract.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/indexing/scip_pipeline.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/c.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/cpp.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/dart.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/elixir.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/go.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/haskell.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/javascript.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/perl.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/python.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/ruby.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/rust.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/typescript.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/scip_indexer.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/scip_pb2.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/tools/tree_sitter_parser.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/utils/path_ignore.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/utils/repo_path.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/src/codegraphcontext/utils/visualize_graph.py +0 -0
- {codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/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.15"
|
|
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.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/lang/java.rs
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use std::collections::HashSet;
|
|
1
|
+
use std::collections::{HashMap, HashSet};
|
|
2
2
|
|
|
3
3
|
use streaming_iterator::StreamingIterator;
|
|
4
4
|
use tree_sitter::{Language, Node, Query, QueryCursor};
|
|
@@ -26,6 +26,10 @@ const QUERY_FUNCTIONS: &str = r#"
|
|
|
26
26
|
name: (identifier) @name
|
|
27
27
|
parameters: (formal_parameters) @params
|
|
28
28
|
) @function_node
|
|
29
|
+
|
|
30
|
+
(compact_constructor_declaration
|
|
31
|
+
name: (identifier) @name
|
|
32
|
+
) @function_node
|
|
29
33
|
"#;
|
|
30
34
|
|
|
31
35
|
const QUERY_CLASSES: &str = r#"
|
|
@@ -33,6 +37,7 @@ const QUERY_CLASSES: &str = r#"
|
|
|
33
37
|
(class_declaration name: (identifier) @name)
|
|
34
38
|
(interface_declaration name: (identifier) @name)
|
|
35
39
|
(enum_declaration name: (identifier) @name)
|
|
40
|
+
(record_declaration name: (identifier) @name)
|
|
36
41
|
(annotation_type_declaration name: (identifier) @name)
|
|
37
42
|
] @class
|
|
38
43
|
"#;
|
|
@@ -74,8 +79,42 @@ const QUERY_VARIABLES: &str = r#"
|
|
|
74
79
|
const QUERY_PRE_SCAN: &str = r#"
|
|
75
80
|
(class_declaration name: (identifier) @name)
|
|
76
81
|
(interface_declaration name: (identifier) @name)
|
|
82
|
+
(enum_declaration name: (identifier) @name)
|
|
83
|
+
(record_declaration name: (identifier) @name)
|
|
84
|
+
(annotation_type_declaration name: (identifier) @name)
|
|
85
|
+
"#;
|
|
86
|
+
|
|
87
|
+
/// Typed declarations used to infer the static type of a call receiver
|
|
88
|
+
/// (`service.upsert()` → `AccumulatePointService`).
|
|
89
|
+
const QUERY_TYPED_NAMES: &str = r#"
|
|
90
|
+
(field_declaration
|
|
91
|
+
type: (_) @type
|
|
92
|
+
declarator: (variable_declarator name: (identifier) @name))
|
|
93
|
+
(local_variable_declaration
|
|
94
|
+
type: (_) @type
|
|
95
|
+
declarator: (variable_declarator name: (identifier) @name))
|
|
96
|
+
(formal_parameter
|
|
97
|
+
type: (_) @type
|
|
98
|
+
name: (identifier) @name)
|
|
77
99
|
"#;
|
|
78
100
|
|
|
101
|
+
/// Node kinds that declare a type (and therefore own fields/methods).
|
|
102
|
+
const TYPE_DECL_KINDS: &[&str] = &[
|
|
103
|
+
"class_declaration",
|
|
104
|
+
"interface_declaration",
|
|
105
|
+
"enum_declaration",
|
|
106
|
+
"record_declaration",
|
|
107
|
+
"annotation_type_declaration",
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
/// Node kinds that scope local variables and parameters.
|
|
111
|
+
const CALLABLE_KINDS: &[&str] = &[
|
|
112
|
+
"method_declaration",
|
|
113
|
+
"constructor_declaration",
|
|
114
|
+
"compact_constructor_declaration",
|
|
115
|
+
"lambda_expression",
|
|
116
|
+
];
|
|
117
|
+
|
|
79
118
|
pub struct JavaExtractor;
|
|
80
119
|
|
|
81
120
|
impl JavaExtractor {
|
|
@@ -112,9 +151,11 @@ impl JavaExtractor {
|
|
|
112
151
|
let types = &[
|
|
113
152
|
"method_declaration",
|
|
114
153
|
"constructor_declaration",
|
|
154
|
+
"compact_constructor_declaration",
|
|
115
155
|
"class_declaration",
|
|
116
156
|
"interface_declaration",
|
|
117
157
|
"enum_declaration",
|
|
158
|
+
"record_declaration",
|
|
118
159
|
"annotation_type_declaration",
|
|
119
160
|
];
|
|
120
161
|
get_parent_context(node, source, types)
|
|
@@ -125,16 +166,94 @@ impl JavaExtractor {
|
|
|
125
166
|
node: &Node,
|
|
126
167
|
source: &[u8],
|
|
127
168
|
) -> (Option<String>, Option<String>) {
|
|
128
|
-
let
|
|
129
|
-
"class_declaration",
|
|
130
|
-
"interface_declaration",
|
|
131
|
-
"enum_declaration",
|
|
132
|
-
"annotation_type_declaration",
|
|
133
|
-
];
|
|
134
|
-
let (name, kind, _) = get_parent_context(node, source, types);
|
|
169
|
+
let (name, kind, _) = get_parent_context(node, source, TYPE_DECL_KINDS);
|
|
135
170
|
(name, kind)
|
|
136
171
|
}
|
|
137
172
|
|
|
173
|
+
/// Reduce a declared type to the simple class name used for lookup:
|
|
174
|
+
/// `List<Foo>` → `List`, `a.b.Foo` → `Foo`, `Foo[]` → `Foo`.
|
|
175
|
+
/// Returns None for `var` and primitives, which carry no useful type.
|
|
176
|
+
fn simple_type_name(type_text: &str) -> Option<String> {
|
|
177
|
+
let base = type_text.split('<').next().unwrap_or(type_text);
|
|
178
|
+
let base = base.trim_end_matches("[]").trim();
|
|
179
|
+
let base = base.rsplit('.').next().unwrap_or(base).trim();
|
|
180
|
+
if base.is_empty() || base == "var" || !base.starts_with(|c: char| c.is_ascii_uppercase()) {
|
|
181
|
+
return None;
|
|
182
|
+
}
|
|
183
|
+
Some(base.to_string())
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Start byte of the closest ancestor of one of `kinds`, if any.
|
|
187
|
+
fn enclosing_scope(node: &Node, kinds: &[&str]) -> Option<usize> {
|
|
188
|
+
let mut curr = node.parent();
|
|
189
|
+
while let Some(p) = curr {
|
|
190
|
+
if kinds.contains(&p.kind()) {
|
|
191
|
+
return Some(p.start_byte());
|
|
192
|
+
}
|
|
193
|
+
curr = p.parent();
|
|
194
|
+
}
|
|
195
|
+
None
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// Map `(scope, variable name) → simple type name`. The scope is the
|
|
199
|
+
/// start byte of the enclosing callable for locals/parameters, or of the
|
|
200
|
+
/// enclosing type declaration for fields.
|
|
201
|
+
fn collect_typed_names(&self, root: &Node, source: &[u8]) -> HashMap<(usize, String), String> {
|
|
202
|
+
let mut map = HashMap::new();
|
|
203
|
+
let lang = self.language();
|
|
204
|
+
let query = match Query::new(&lang, QUERY_TYPED_NAMES) {
|
|
205
|
+
Ok(q) => q,
|
|
206
|
+
Err(_) => return map,
|
|
207
|
+
};
|
|
208
|
+
let names = query.capture_names();
|
|
209
|
+
let mut cursor = QueryCursor::new();
|
|
210
|
+
let mut matches = cursor.matches(&query, *root, source);
|
|
211
|
+
while let Some(m) = matches.next() {
|
|
212
|
+
let mut ty = None;
|
|
213
|
+
let mut name_node = None;
|
|
214
|
+
for cap in m.captures {
|
|
215
|
+
match names[cap.index as usize] {
|
|
216
|
+
"type" => ty = Some(cap.node),
|
|
217
|
+
"name" => name_node = Some(cap.node),
|
|
218
|
+
_ => {}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
let (Some(ty), Some(name_node)) = (ty, name_node) else { continue };
|
|
222
|
+
let Some(simple) = Self::simple_type_name(get_node_text(&ty, source)) else { continue };
|
|
223
|
+
let is_field = name_node.parent().and_then(|p| p.parent()).map(|d| d.kind())
|
|
224
|
+
== Some("field_declaration");
|
|
225
|
+
let scope = if is_field {
|
|
226
|
+
Self::enclosing_scope(&name_node, TYPE_DECL_KINDS)
|
|
227
|
+
} else {
|
|
228
|
+
Self::enclosing_scope(&name_node, CALLABLE_KINDS)
|
|
229
|
+
.or_else(|| Self::enclosing_scope(&name_node, TYPE_DECL_KINDS))
|
|
230
|
+
};
|
|
231
|
+
let Some(scope) = scope else { continue };
|
|
232
|
+
map.entry((scope, get_node_text(&name_node, source).to_string()))
|
|
233
|
+
.or_insert(simple);
|
|
234
|
+
}
|
|
235
|
+
map
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// Resolve a receiver variable to its declared type, searching the
|
|
239
|
+
/// enclosing callables (innermost first), then enclosing type declarations.
|
|
240
|
+
fn lookup_receiver_type(
|
|
241
|
+
node: &Node,
|
|
242
|
+
var: &str,
|
|
243
|
+
typed: &HashMap<(usize, String), String>,
|
|
244
|
+
) -> Option<String> {
|
|
245
|
+
let mut curr = node.parent();
|
|
246
|
+
while let Some(p) = curr {
|
|
247
|
+
if CALLABLE_KINDS.contains(&p.kind()) || TYPE_DECL_KINDS.contains(&p.kind()) {
|
|
248
|
+
if let Some(t) = typed.get(&(p.start_byte(), var.to_string())) {
|
|
249
|
+
return Some(t.clone());
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
curr = p.parent();
|
|
253
|
+
}
|
|
254
|
+
None
|
|
255
|
+
}
|
|
256
|
+
|
|
138
257
|
/// Extract parameter names from a formal_parameters text like "(int x, String name)"
|
|
139
258
|
fn extract_parameter_names(&self, params_text: &str) -> Vec<String> {
|
|
140
259
|
let mut params = Vec::new();
|
|
@@ -317,11 +436,7 @@ impl LanguageExtractor for JavaExtractor {
|
|
|
317
436
|
}
|
|
318
437
|
}
|
|
319
438
|
|
|
320
|
-
let (context, _, _) = get_parent_context(
|
|
321
|
-
&node,
|
|
322
|
-
source,
|
|
323
|
-
&["class_declaration", "interface_declaration"],
|
|
324
|
-
);
|
|
439
|
+
let (context, _, _) = get_parent_context(&node, source, TYPE_DECL_KINDS);
|
|
325
440
|
|
|
326
441
|
let mut class = ClassData {
|
|
327
442
|
name: class_name,
|
|
@@ -391,6 +506,7 @@ impl LanguageExtractor for JavaExtractor {
|
|
|
391
506
|
fn find_calls(&self, root: &Node, source: &[u8]) -> Vec<CallData> {
|
|
392
507
|
let mut calls = Vec::new();
|
|
393
508
|
let mut seen_calls: HashSet<String> = HashSet::new();
|
|
509
|
+
let typed_names = self.collect_typed_names(root, source);
|
|
394
510
|
|
|
395
511
|
for (node, capture_name) in self.execute_query(QUERY_CALLS, root, source) {
|
|
396
512
|
if capture_name != "name" {
|
|
@@ -449,10 +565,15 @@ impl LanguageExtractor for JavaExtractor {
|
|
|
449
565
|
if let Some(obj_node) = call_node.child_by_field_name("object") {
|
|
450
566
|
let obj_text = get_node_text(&obj_node, source);
|
|
451
567
|
full_name = format!("{}.{}", obj_text, call_name);
|
|
452
|
-
//
|
|
568
|
+
// Resolve the receiver variable to its declared type when
|
|
569
|
+
// known; otherwise keep the identifier (e.g. a static
|
|
570
|
+
// `Foo.bar()` call, where the identifier is the type).
|
|
453
571
|
let base_obj = obj_text.split('.').next().unwrap_or(obj_text);
|
|
454
572
|
if !base_obj.contains('(') {
|
|
455
|
-
inferred_obj_type = Some(
|
|
573
|
+
inferred_obj_type = Some(
|
|
574
|
+
Self::lookup_receiver_type(&node, base_obj, &typed_names)
|
|
575
|
+
.unwrap_or_else(|| base_obj.to_string()),
|
|
576
|
+
);
|
|
456
577
|
}
|
|
457
578
|
}
|
|
458
579
|
} else if call_node.kind() == "object_creation_expression" {
|
|
@@ -666,4 +787,55 @@ public interface MyInterface {
|
|
|
666
787
|
assert!(names.contains(&"MyService".to_string()));
|
|
667
788
|
assert!(names.contains(&"MyInterface".to_string()));
|
|
668
789
|
}
|
|
790
|
+
|
|
791
|
+
#[test]
|
|
792
|
+
fn test_find_records() {
|
|
793
|
+
let code = r#"
|
|
794
|
+
public record CreateOrderRequest(String customerId, int qty) implements Request {
|
|
795
|
+
public CreateOrderRequest {
|
|
796
|
+
if (qty < 0) throw new IllegalArgumentException();
|
|
797
|
+
}
|
|
798
|
+
public boolean isBulk() { return qty > 10; }
|
|
799
|
+
}
|
|
800
|
+
"#;
|
|
801
|
+
let (tree, source) = parse_source(code);
|
|
802
|
+
let ext = JavaExtractor;
|
|
803
|
+
let classes = ext.find_classes(&tree.root_node(), &source, false);
|
|
804
|
+
assert_eq!(classes.len(), 1);
|
|
805
|
+
assert_eq!(classes[0].name, "CreateOrderRequest");
|
|
806
|
+
assert_eq!(classes[0].bases, vec!["Request".to_string()]);
|
|
807
|
+
|
|
808
|
+
let functions = ext.find_functions(&tree.root_node(), &source, false);
|
|
809
|
+
let is_bulk = functions.iter().find(|f| f.name == "isBulk").unwrap();
|
|
810
|
+
assert_eq!(is_bulk.class_context.as_deref(), Some("CreateOrderRequest"));
|
|
811
|
+
assert!(functions.iter().any(|f| f.name == "CreateOrderRequest"));
|
|
812
|
+
|
|
813
|
+
let names = ext.pre_scan_definitions(&tree.root_node(), &source);
|
|
814
|
+
assert!(names.contains(&"CreateOrderRequest".to_string()));
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
#[test]
|
|
818
|
+
fn test_call_receiver_type_inferred_from_declarations() {
|
|
819
|
+
let code = r#"
|
|
820
|
+
public class PointController {
|
|
821
|
+
private final PointService service;
|
|
822
|
+
public Resp upsert(Req req) {
|
|
823
|
+
List<String> items = new ArrayList<>();
|
|
824
|
+
items.add("x");
|
|
825
|
+
req.validate();
|
|
826
|
+
Helper.run();
|
|
827
|
+
return service.upsert(req);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
"#;
|
|
831
|
+
let (tree, source) = parse_source(code);
|
|
832
|
+
let calls = JavaExtractor.find_calls(&tree.root_node(), &source);
|
|
833
|
+
let ty = |full: &str| {
|
|
834
|
+
calls.iter().find(|c| c.full_name == full).unwrap().inferred_obj_type.clone()
|
|
835
|
+
};
|
|
836
|
+
assert_eq!(ty("service.upsert").as_deref(), Some("PointService")); // field
|
|
837
|
+
assert_eq!(ty("items.add").as_deref(), Some("List")); // local, generics stripped
|
|
838
|
+
assert_eq!(ty("req.validate").as_deref(), Some("Req")); // parameter
|
|
839
|
+
assert_eq!(ty("Helper.run").as_deref(), Some("Helper")); // static call
|
|
840
|
+
}
|
|
669
841
|
}
|
{codegraphcontext_rust-0.9.14 → codegraphcontext_rust-0.9.15}/rust/cgc-core/src/resolution/calls.rs
RENAMED
|
@@ -97,6 +97,18 @@ pub fn resolve_function_call(
|
|
|
97
97
|
base_obj.unwrap_or(called_name.as_str())
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
+
// `obj.method()` on a receiver other than self/this: the target lives on
|
|
101
|
+
// obj's type, never on a same-named function of the caller's file.
|
|
102
|
+
let qualified_other = base_obj.map_or(false, |b| !b.is_empty() && !self_receivers.contains(&b));
|
|
103
|
+
// Receiver whose (capitalised) type is known but defined outside the repo,
|
|
104
|
+
// e.g. `list.get()` with `List<Foo> list` — don't guess a target by name.
|
|
105
|
+
let external_receiver_type = qualified_other
|
|
106
|
+
&& call.inferred_obj_type.as_deref().map_or(false, |t| {
|
|
107
|
+
t.starts_with(|c: char| c.is_ascii_uppercase())
|
|
108
|
+
&& !imports_map.contains_key(t)
|
|
109
|
+
&& !local_names.contains(t)
|
|
110
|
+
});
|
|
111
|
+
|
|
100
112
|
let mut resolved_path: Option<String> = None;
|
|
101
113
|
let mut is_unresolved_external = false;
|
|
102
114
|
|
|
@@ -150,17 +162,37 @@ pub fn resolve_function_call(
|
|
|
150
162
|
|
|
151
163
|
// 5. Fallback: try called_name directly
|
|
152
164
|
if resolved_path.is_none() {
|
|
153
|
-
if
|
|
165
|
+
if qualified_other && (external_receiver_type || !imports_map.contains_key(called_name.as_str())) {
|
|
166
|
+
// Unresolvable receiver call. Pointing it at the caller's file
|
|
167
|
+
// would bind it to a same-named local function (false recursion),
|
|
168
|
+
// so drop it in that case; otherwise keep it as external.
|
|
169
|
+
if local_names.contains(called_name.as_str()) {
|
|
170
|
+
return None;
|
|
171
|
+
}
|
|
172
|
+
resolved_path = Some(caller_file_path.to_string());
|
|
173
|
+
} else if !qualified_other && local_names.contains(called_name.as_str()) {
|
|
154
174
|
resolved_path = Some(caller_file_path.to_string());
|
|
155
175
|
is_unresolved_external = false;
|
|
156
|
-
} else if let Some(
|
|
176
|
+
} else if let Some(all_candidates) = imports_map.get(called_name.as_str()) {
|
|
177
|
+
// A receiver call can't target the caller's own namesake.
|
|
178
|
+
let candidates: Vec<&String> = all_candidates
|
|
179
|
+
.iter()
|
|
180
|
+
.filter(|p| {
|
|
181
|
+
!(qualified_other
|
|
182
|
+
&& p.as_str() == caller_file_path
|
|
183
|
+
&& local_names.contains(called_name.as_str()))
|
|
184
|
+
})
|
|
185
|
+
.collect();
|
|
186
|
+
if candidates.is_empty() && qualified_other && local_names.contains(called_name.as_str()) {
|
|
187
|
+
return None;
|
|
188
|
+
}
|
|
157
189
|
if !candidates.is_empty() {
|
|
158
190
|
// Try matching via local imports
|
|
159
191
|
let mut found = false;
|
|
160
|
-
for p in candidates {
|
|
192
|
+
for p in &candidates {
|
|
161
193
|
for imp_name in local_imports.values() {
|
|
162
194
|
if p.contains(&imp_name.replace('.', "/")) {
|
|
163
|
-
resolved_path = Some(p.clone());
|
|
195
|
+
resolved_path = Some((*p).clone());
|
|
164
196
|
is_unresolved_external = false;
|
|
165
197
|
found = true;
|
|
166
198
|
break;
|
|
@@ -171,7 +203,7 @@ pub fn resolve_function_call(
|
|
|
171
203
|
}
|
|
172
204
|
}
|
|
173
205
|
if resolved_path.is_none() {
|
|
174
|
-
resolved_path = Some(candidates[0].
|
|
206
|
+
resolved_path = Some(candidates[0].to_string());
|
|
175
207
|
}
|
|
176
208
|
}
|
|
177
209
|
} else {
|
|
@@ -485,4 +517,72 @@ mod tests {
|
|
|
485
517
|
assert_eq!(groups.fn_to_cls.len(), 1);
|
|
486
518
|
assert_eq!(groups.fn_to_cls[0].called_name, "Helper");
|
|
487
519
|
}
|
|
520
|
+
|
|
521
|
+
fn receiver_call(name: &str, full: &str, obj_type: Option<&str>) -> CallInput {
|
|
522
|
+
CallInput {
|
|
523
|
+
name: name.to_string(),
|
|
524
|
+
full_name: full.to_string(),
|
|
525
|
+
line_number: 10,
|
|
526
|
+
args: vec![],
|
|
527
|
+
inferred_obj_type: obj_type.map(|s| s.to_string()),
|
|
528
|
+
context_name: Some(name.to_string()),
|
|
529
|
+
context_type: Some("method_declaration".to_string()),
|
|
530
|
+
context_line: Some(9),
|
|
531
|
+
class_context_name: None,
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
#[test]
|
|
536
|
+
fn test_receiver_call_resolves_via_inferred_type_not_local_namesake() {
|
|
537
|
+
// Controller.upsert() { return service.upsert(req); }
|
|
538
|
+
let call = receiver_call("upsert", "service.upsert", Some("PointService"));
|
|
539
|
+
let local_names: HashSet<String> = ["upsert".to_string()].into();
|
|
540
|
+
let mut imports_map = HashMap::new();
|
|
541
|
+
imports_map.insert("PointService".to_string(), vec!["/repo/PointService.java".to_string()]);
|
|
542
|
+
|
|
543
|
+
let r = resolve_function_call(
|
|
544
|
+
&call, "/repo/PointController.java", &local_names, &HashMap::new(), &imports_map, false,
|
|
545
|
+
)
|
|
546
|
+
.unwrap();
|
|
547
|
+
assert_eq!(r.called_file_path, "/repo/PointService.java");
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
#[test]
|
|
551
|
+
fn test_unresolved_receiver_call_is_not_bound_to_local_namesake() {
|
|
552
|
+
// list.add(x) inside a local add(): must not become add → add.
|
|
553
|
+
let call = receiver_call("add", "items.add", Some("List"));
|
|
554
|
+
let local_names: HashSet<String> = ["add".to_string()].into();
|
|
555
|
+
let r = resolve_function_call(
|
|
556
|
+
&call, "/repo/Cart.java", &local_names, &HashMap::new(), &HashMap::new(), false,
|
|
557
|
+
);
|
|
558
|
+
assert!(r.is_none());
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
#[test]
|
|
562
|
+
fn test_this_receiver_still_resolves_locally() {
|
|
563
|
+
let call = receiver_call("helper", "this.helper", None);
|
|
564
|
+
let local_names: HashSet<String> = ["helper".to_string()].into();
|
|
565
|
+
let r = resolve_function_call(
|
|
566
|
+
&call, "/repo/A.java", &local_names, &HashMap::new(), &HashMap::new(), false,
|
|
567
|
+
)
|
|
568
|
+
.unwrap();
|
|
569
|
+
assert_eq!(r.called_file_path, "/repo/A.java");
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
#[test]
|
|
573
|
+
fn test_receiver_call_candidates_skip_caller_namesake() {
|
|
574
|
+
// BaseCrudService.findChangesSince() { getRepository().findChangesSince(..) }
|
|
575
|
+
let call = receiver_call("findChangesSince", "getRepository().findChangesSince", None);
|
|
576
|
+
let local_names: HashSet<String> = ["findChangesSince".to_string()].into();
|
|
577
|
+
let mut imports_map = HashMap::new();
|
|
578
|
+
imports_map.insert(
|
|
579
|
+
"findChangesSince".to_string(),
|
|
580
|
+
vec!["/repo/BaseCrudService.java".to_string(), "/repo/Repo.java".to_string()],
|
|
581
|
+
);
|
|
582
|
+
let r = resolve_function_call(
|
|
583
|
+
&call, "/repo/BaseCrudService.java", &local_names, &HashMap::new(), &imports_map, false,
|
|
584
|
+
)
|
|
585
|
+
.unwrap();
|
|
586
|
+
assert_eq!(r.called_file_path, "/repo/Repo.java");
|
|
587
|
+
}
|
|
488
588
|
}
|