graphlint 0.2.0__tar.gz → 0.3.0__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.
Files changed (117) hide show
  1. {graphlint-0.2.0 → graphlint-0.3.0}/CHANGELOG.md +11 -0
  2. {graphlint-0.2.0/graphlint.egg-info → graphlint-0.3.0}/PKG-INFO +35 -9
  3. {graphlint-0.2.0 → graphlint-0.3.0}/README.md +31 -8
  4. {graphlint-0.2.0 → graphlint-0.3.0}/README_PYPI.md +31 -8
  5. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/architecture/overview.md +3 -3
  6. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/cli/usage.md +1 -0
  7. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/guide/configuration.md +36 -12
  8. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/guide/entry-detection.md +126 -10
  9. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/guide/getting-started.md +7 -1
  10. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/README.md +30 -7
  11. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/architecture/overview.md +4 -4
  12. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/cli/usage.md +1 -0
  13. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/guide/configuration.md +36 -12
  14. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/guide/entry-detection.md +126 -10
  15. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/guide/getting-started.md +7 -1
  16. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/__init__.py +1 -1
  17. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/_graph_algo.py +37 -22
  18. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/graph.py +15 -7
  19. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/__init__.py +5 -1
  20. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/visitor.py +28 -6
  21. graphlint-0.3.0/graphlint/analyzer/language/rust/__init__.py +72 -0
  22. graphlint-0.3.0/graphlint/analyzer/language/rust/constants.py +258 -0
  23. graphlint-0.3.0/graphlint/analyzer/language/rust/entry.py +332 -0
  24. graphlint-0.3.0/graphlint/analyzer/language/rust/imports.py +171 -0
  25. graphlint-0.3.0/graphlint/analyzer/language/rust/parser.py +191 -0
  26. graphlint-0.3.0/graphlint/analyzer/language/rust/visitor.py +871 -0
  27. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/api.py +55 -8
  28. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/config/defaults.py +65 -1
  29. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/i18n/en.py +1 -0
  30. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/i18n/zh_CN.py +1 -0
  31. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/incremental/indexer.py +43 -6
  32. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/params.py +9 -0
  33. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/storage/hashing.py +4 -1
  34. {graphlint-0.2.0 → graphlint-0.3.0/graphlint.egg-info}/PKG-INFO +35 -9
  35. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint.egg-info/SOURCES.txt +6 -0
  36. graphlint-0.3.0/graphlint.egg-info/requires.txt +8 -0
  37. {graphlint-0.2.0 → graphlint-0.3.0}/pyproject.toml +5 -1
  38. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_auto_build.py +1 -1
  39. graphlint-0.2.0/graphlint.egg-info/requires.txt +0 -4
  40. {graphlint-0.2.0 → graphlint-0.3.0}/CONTRIBUTING.md +0 -0
  41. {graphlint-0.2.0 → graphlint-0.3.0}/LICENSE +0 -0
  42. {graphlint-0.2.0 → graphlint-0.3.0}/MANIFEST.in +0 -0
  43. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/api/build.md +0 -0
  44. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/api/configure.md +0 -0
  45. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/api/exceptions.md +0 -0
  46. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/api/query.md +0 -0
  47. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/guide/agent-integration.md +0 -0
  48. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/guide/warnings.md +0 -0
  49. {graphlint-0.2.0 → graphlint-0.3.0}/docs/en/index.md +0 -0
  50. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/api/build.md +0 -0
  51. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/api/configure.md +0 -0
  52. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/api/exceptions.md +0 -0
  53. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/api/query.md +0 -0
  54. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/guide/agent-integration.md +0 -0
  55. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/guide/warnings.md +0 -0
  56. {graphlint-0.2.0 → graphlint-0.3.0}/docs/zh/index.md +0 -0
  57. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/agent_tools.py +0 -0
  58. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/__init__.py +0 -0
  59. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/_types.py +0 -0
  60. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/__init__.py +0 -0
  61. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/base.py +0 -0
  62. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/constants.py +0 -0
  63. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/decorators.py +0 -0
  64. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/entry.py +0 -0
  65. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/imports.py +0 -0
  66. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/python/parser.py +0 -0
  67. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/language/registry.py +0 -0
  68. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/analyzer/warnings.py +0 -0
  69. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/cli.py +0 -0
  70. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/config/__init__.py +0 -0
  71. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/config/manager.py +0 -0
  72. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/exceptions.py +0 -0
  73. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/i18n/__init__.py +0 -0
  74. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/incremental/__init__.py +0 -0
  75. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/incremental/_db_ops.py +0 -0
  76. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/query/__init__.py +0 -0
  77. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/query/engine.py +0 -0
  78. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/query/formatter.py +0 -0
  79. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/query/volume.py +0 -0
  80. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/storage/__init__.py +0 -0
  81. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/storage/db.py +0 -0
  82. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint/storage/schema.py +0 -0
  83. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint.egg-info/dependency_links.txt +0 -0
  84. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint.egg-info/entry_points.txt +0 -0
  85. {graphlint-0.2.0 → graphlint-0.3.0}/graphlint.egg-info/top_level.txt +0 -0
  86. {graphlint-0.2.0 → graphlint-0.3.0}/setup.cfg +0 -0
  87. {graphlint-0.2.0 → graphlint-0.3.0}/tests/__init__.py +0 -0
  88. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/__init__.py +0 -0
  89. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_api.py +0 -0
  90. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_cli.py +0 -0
  91. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_data_transfer.py +0 -0
  92. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_dead_code_query.py +0 -0
  93. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_full_pipeline.py +0 -0
  94. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_incremental.py +0 -0
  95. {graphlint-0.2.0 → graphlint-0.3.0}/tests/integration/test_json_output.py +0 -0
  96. {graphlint-0.2.0 → graphlint-0.3.0}/tests/performance/__init__.py +0 -0
  97. {graphlint-0.2.0 → graphlint-0.3.0}/tests/performance/test_large_codebase.py +0 -0
  98. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/__init__.py +0 -0
  99. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_config.py +0 -0
  100. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_db.py +0 -0
  101. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_db_ops.py +0 -0
  102. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_decorators.py +0 -0
  103. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_engine.py +0 -0
  104. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_entry_detect.py +0 -0
  105. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_exceptions.py +0 -0
  106. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_formatter.py +0 -0
  107. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_graph.py +0 -0
  108. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_graph_algo.py +0 -0
  109. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_hashing.py +0 -0
  110. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_i18n.py +0 -0
  111. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_imports.py +0 -0
  112. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_mock_boundary.py +0 -0
  113. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_params.py +0 -0
  114. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_parser.py +0 -0
  115. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_schema.py +0 -0
  116. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_volume.py +0 -0
  117. {graphlint-0.2.0 → graphlint-0.3.0}/tests/unit/test_warnings.py +0 -0
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.3.0] - 2026-07-23
6
+
7
+ ### Added
8
+ - Experimental Rust language analysis via `tree-sitter-rust` (opt-in via `[rust]`
9
+ optional dependency group)
10
+ - `query --public-as-entry` option: treats all public functions/methods as entry
11
+ points for dead-code reachability analysis
12
+ - `pub_api` entry rule template (disabled by default) for projects that want
13
+ public API surface as the primary entry
14
+
15
+
5
16
  ## [0.2.0] - 2026-07-22
6
17
 
7
18
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphlint
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A static analysis tool for identifying dead code in a codebase. It provides Python API and CLI for use by agents.
5
5
  Author-email: Yutong Zou <yutong.zou.24@alumni.ucl.ac.uk>
6
6
  License-Expression: MIT
@@ -27,6 +27,9 @@ License-File: LICENSE
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: pytest>=7; extra == "dev"
29
29
  Requires-Dist: pytest-cov>=4; extra == "dev"
30
+ Provides-Extra: rust
31
+ Requires-Dist: tree-sitter<0.25,>=0.22; extra == "rust"
32
+ Requires-Dist: tree-sitter-rust<0.25,>=0.22; extra == "rust"
30
33
  Dynamic: license-file
31
34
 
32
35
  # graphlint
@@ -35,20 +38,31 @@ Dynamic: license-file
35
38
  [![Python](https://img.shields.io/pypi/pyversions/graphlint)](https://pypi.org/project/graphlint/)
36
39
  [![License](https://img.shields.io/pypi/l/graphlint)](https://github.com/AngelosZou/graphlint/blob/main/LICENSE)
37
40
 
38
- [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/AngelosZou/graphlint/blob/main/README.md)
39
- [![zh](https://img.shields.io/badge/lang-zh--CN-blue.svg)](https://github.com/AngelosZou/graphlint/blob/main/docs/zh/README.md)
41
+ [English](https://github.com/AngelosZou/graphlint/blob/main/README.md) | [简体中文](https://github.com/AngelosZou/graphlint/blob/main/docs/zh/README.md)
40
42
 
41
- **Dead code detection for AI-generated Python codebases.**
43
+ **Dead code detection for AI-generated codebases.**
42
44
 
43
- AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your Python codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
45
+ AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
46
+
47
+ ## Supported Languages
48
+
49
+ | Language | Status | Parser | Features |
50
+ |----------|--------|--------|----------|
51
+ | **Python** (`.py`) | Built-in | `ast` (stdlib) | Decorators, type annotations, dynamic imports, framework-aware entry detection |
52
+ | **Rust** (`.rs`) | Built-in (opt-in deps) | `tree-sitter` | Attribute macros, traits, `pub` visibility, `macro_rules!` |
53
+
54
+ Install with Rust support: `pip install graphlint[rust]` (adds `tree-sitter` and `tree-sitter-rust`).
44
55
 
45
56
  ## Features
46
57
 
47
58
  - **Dead code detection** — finds components unreachable from any entry point via graph traversal
48
- - **AST parsing** — extracts classes, functions, methods, variables, and fields; aware of type annotations and unpacked variables (e.g., loop bindings)
59
+ - **Multi-language support** — Python and Rust backends via a language adapter abstraction; Python uses stdlib `ast`, Rust uses `tree-sitter`
60
+ - **Language-specific awareness** — Python decorators, Rust attribute macros (`#[tokio::main]`, `#[test]`), trait implementations, `pub` visibility, and more
61
+ - **AST/CST parsing** — extracts functions, methods, structs, enums, traits, impls, macros, variables, and fields; aware of type annotations and unpacked variables
49
62
  - **Dependency graph** — builds directed edges: `read`, `write`, `call`, `inherit`, `decorate`
50
- - **Entry point detection** — 10 built-in rules (main, FastAPI, Flask, Django, Click, Typer, Celery, pytest, plus package and test entries) and custom rules
51
- - **Multi-language architecture** — language adapter abstraction layer, laying the foundation for future multi-language support
63
+ - **Entry point detection** — 17 built-in rules covering Python frameworks (FastAPI, Flask, Django, Click, Typer, Celery, pytest) and Rust conventions (main, async runtimes, WASM, proc macros, FFI, tests, pub API) plus custom rules
64
+ - **Configurable entry templates** — add custom entry rules via `ast_pattern` prefixes including `function_call:`, `function_def:`, `decorator:`, `file_match:`, `visibility:pub` (Rust), `trait_impl:` (Rust), `macro_def:` (Rust), and more
65
+ - **`--public-as-entry` flag** — treat all public items (Rust `pub`) as entry points for library-crate analysis
52
66
  - **Warning detection** — 11 warning types including circular references, unused imports, write-only variables, and more
53
67
  - **Python API + CLI** — integrate into any Tool, CI pipeline, or let agents self-analyze and self-clean
54
68
 
@@ -60,6 +74,12 @@ pip install graphlint
60
74
 
61
75
  **Requirements:** Python >= 3.9
62
76
 
77
+ For Rust support (`.rs` files), install the optional `tree-sitter` dependencies:
78
+
79
+ ```bash
80
+ pip install graphlint[rust]
81
+ ```
82
+
63
83
  ## Quick Start
64
84
 
65
85
  ### Agent Integration
@@ -96,6 +116,9 @@ graphlint query -g 1 --detail full
96
116
  # Exit non-zero when dead code or circular refs found (for CI)
97
117
  graphlint query --json --fail-on dead_code,circular_ref
98
118
 
119
+ # Treat all public items as entry points (library-crate mode)
120
+ graphlint query --public-as-entry
121
+
99
122
  # Rebuild index
100
123
  graphlint build --force
101
124
 
@@ -193,7 +216,10 @@ Full documentation is available in the [docs/](https://github.com/AngelosZou/gra
193
216
 
194
217
  ## Limitations
195
218
 
196
- - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
219
+ - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. This primarily affects Python; Rust's static dispatch model produces fewer false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
220
+ - **Python dynamic imports** — due to Python's dynamic import mechanisms (`importlib`, `getattr`, metaclasses, etc.), the default entry templates may produce false positives in codebases that rely heavily on runtime dispatch. Users should tune the `entry_rules` configuration to match their project's conventions.
221
+ - **Rust macro expansion** — tree-sitter parses unexpanded source; procedural macros and `macro_rules!` bodies appear as opaque token trees. Some macro-generated call paths may be missed. `#[derive]` attributes are partially recognized via implicit `inherit` edges.
222
+ - **`--public-as-entry` scope** — this flag only applies to languages with `public` visibility declarations (Rust `pub`). It has no effect on Python files. Toggling this flag triggers a full re-index. For long-term library-crate analysis, prefer enabling the `rust_pub_api` entry rule via `graphlint config` to persist the setting.
197
223
  - **Large codebase build time** — on a large codebase with 700+ `.py` files, 1,000+ classes, and 14,000+ functions, a full rebuild takes approximately 200 seconds (actual performance depends on hardware). Small projects (~60 files) complete in ~1 second. **Best practice:** run `query` before making changes to plan your work, and avoid invoking `query` during refactoring to prevent unnecessary index rebuilds.
198
224
 
199
225
  ## License
@@ -4,20 +4,31 @@
4
4
  [![Python](https://img.shields.io/pypi/pyversions/graphlint)](https://pypi.org/project/graphlint/)
5
5
  [![License](https://img.shields.io/pypi/l/graphlint)](https://github.com/AngelosZou/graphlint/blob/main/LICENSE)
6
6
 
7
- [![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
8
- [![zh](https://img.shields.io/badge/lang-zh--CN-blue.svg)](docs/zh/README.md)
7
+ [English](README.md) | [简体中文](docs/zh/README.md)
9
8
 
10
- **Dead code detection for AI-generated Python codebases.**
9
+ **Dead code detection for AI-generated codebases.**
11
10
 
12
- AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your Python codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
11
+ AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
12
+
13
+ ## Supported Languages
14
+
15
+ | Language | Status | Parser | Features |
16
+ |----------|--------|--------|----------|
17
+ | **Python** (`.py`) | Built-in | `ast` (stdlib) | Decorators, type annotations, dynamic imports, framework-aware entry detection |
18
+ | **Rust** (`.rs`) | Built-in (opt-in deps) | `tree-sitter` | Attribute macros, traits, `pub` visibility, `macro_rules!` |
19
+
20
+ Install with Rust support: `pip install graphlint[rust]` (adds `tree-sitter` and `tree-sitter-rust`).
13
21
 
14
22
  ## Features
15
23
 
16
24
  - **Dead code detection** — finds components unreachable from any entry point via graph traversal
17
- - **AST parsing** — extracts classes, functions, methods, variables, and fields; aware of type annotations and unpacked variables (e.g., loop bindings)
25
+ - **Multi-language support** — Python and Rust backends via a language adapter abstraction; Python uses stdlib `ast`, Rust uses `tree-sitter`
26
+ - **Language-specific awareness** — Python decorators, Rust attribute macros (`#[tokio::main]`, `#[test]`), trait implementations, `pub` visibility, and more
27
+ - **AST/CST parsing** — extracts functions, methods, structs, enums, traits, impls, macros, variables, and fields; aware of type annotations and unpacked variables
18
28
  - **Dependency graph** — builds directed edges: `read`, `write`, `call`, `inherit`, `decorate`
19
- - **Entry point detection** — 10 built-in rules (main, FastAPI, Flask, Django, Click, Typer, Celery, pytest, plus package and test entries) and custom rules
20
- - **Multi-language architecture** — language adapter abstraction layer, laying the foundation for future multi-language support
29
+ - **Entry point detection** — 17 built-in rules covering Python frameworks (FastAPI, Flask, Django, Click, Typer, Celery, pytest) and Rust conventions (main, async runtimes, WASM, proc macros, FFI, tests, pub API) plus custom rules
30
+ - **Configurable entry templates** — add custom entry rules via `ast_pattern` prefixes including `function_call:`, `function_def:`, `decorator:`, `file_match:`, `visibility:pub` (Rust), `trait_impl:` (Rust), `macro_def:` (Rust), and more
31
+ - **`--public-as-entry` flag** — treat all public items (Rust `pub`) as entry points for library-crate analysis
21
32
  - **Warning detection** — 11 warning types including circular references, unused imports, write-only variables, and more
22
33
  - **Python API + CLI** — integrate into any Tool, CI pipeline, or let agents self-analyze and self-clean
23
34
 
@@ -29,6 +40,12 @@ pip install graphlint
29
40
 
30
41
  **Requirements:** Python >= 3.9
31
42
 
43
+ For Rust support (`.rs` files), install the optional `tree-sitter` dependencies:
44
+
45
+ ```bash
46
+ pip install graphlint[rust]
47
+ ```
48
+
32
49
  ## Quick Start
33
50
 
34
51
  ### Agent Integration
@@ -65,6 +82,9 @@ graphlint query -g 1 --detail full
65
82
  # Exit non-zero when dead code or circular refs found (for CI)
66
83
  graphlint query --json --fail-on dead_code,circular_ref
67
84
 
85
+ # Treat all public items as entry points (library-crate mode)
86
+ graphlint query --public-as-entry
87
+
68
88
  # Rebuild index
69
89
  graphlint build --force
70
90
 
@@ -162,7 +182,10 @@ Full documentation is available in the [docs/](docs/) directory:
162
182
 
163
183
  ## Limitations
164
184
 
165
- - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
185
+ - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. This primarily affects Python; Rust's static dispatch model produces fewer false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
186
+ - **Python dynamic imports** — due to Python's dynamic import mechanisms (`importlib`, `getattr`, metaclasses, etc.), the default entry templates may produce false positives in codebases that rely heavily on runtime dispatch. Users should tune the `entry_rules` configuration to match their project's conventions.
187
+ - **Rust macro expansion** — tree-sitter parses unexpanded source; procedural macros and `macro_rules!` bodies appear as opaque token trees. Some macro-generated call paths may be missed. `#[derive]` attributes are partially recognized via implicit `inherit` edges.
188
+ - **`--public-as-entry` scope** — this flag only applies to languages with `public` visibility declarations (Rust `pub`). It has no effect on Python files. Toggling this flag triggers a full re-index. For long-term library-crate analysis, prefer enabling the `rust_pub_api` entry rule via `graphlint config` to persist the setting.
166
189
  - **Large codebase build time** — on a large codebase with 700+ `.py` files, 1,000+ classes, and 14,000+ functions, a full rebuild takes approximately 200 seconds (actual performance depends on hardware). Small projects (~60 files) complete in ~1 second. **Best practice:** run `query` before making changes to plan your work, and avoid invoking `query` during refactoring to prevent unnecessary index rebuilds.
167
190
 
168
191
  ## License
@@ -4,20 +4,31 @@
4
4
  [![Python](https://img.shields.io/pypi/pyversions/graphlint)](https://pypi.org/project/graphlint/)
5
5
  [![License](https://img.shields.io/pypi/l/graphlint)](https://github.com/AngelosZou/graphlint/blob/main/LICENSE)
6
6
 
7
- [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/AngelosZou/graphlint/blob/main/README.md)
8
- [![zh](https://img.shields.io/badge/lang-zh--CN-blue.svg)](https://github.com/AngelosZou/graphlint/blob/main/docs/zh/README.md)
7
+ [English](https://github.com/AngelosZou/graphlint/blob/main/README.md) | [简体中文](https://github.com/AngelosZou/graphlint/blob/main/docs/zh/README.md)
9
8
 
10
- **Dead code detection for AI-generated Python codebases.**
9
+ **Dead code detection for AI-generated codebases.**
11
10
 
12
- AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your Python codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
11
+ AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your codebase's dependency graph to identify entry points and **detect dead code** — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
12
+
13
+ ## Supported Languages
14
+
15
+ | Language | Status | Parser | Features |
16
+ |----------|--------|--------|----------|
17
+ | **Python** (`.py`) | Built-in | `ast` (stdlib) | Decorators, type annotations, dynamic imports, framework-aware entry detection |
18
+ | **Rust** (`.rs`) | Built-in (opt-in deps) | `tree-sitter` | Attribute macros, traits, `pub` visibility, `macro_rules!` |
19
+
20
+ Install with Rust support: `pip install graphlint[rust]` (adds `tree-sitter` and `tree-sitter-rust`).
13
21
 
14
22
  ## Features
15
23
 
16
24
  - **Dead code detection** — finds components unreachable from any entry point via graph traversal
17
- - **AST parsing** — extracts classes, functions, methods, variables, and fields; aware of type annotations and unpacked variables (e.g., loop bindings)
25
+ - **Multi-language support** — Python and Rust backends via a language adapter abstraction; Python uses stdlib `ast`, Rust uses `tree-sitter`
26
+ - **Language-specific awareness** — Python decorators, Rust attribute macros (`#[tokio::main]`, `#[test]`), trait implementations, `pub` visibility, and more
27
+ - **AST/CST parsing** — extracts functions, methods, structs, enums, traits, impls, macros, variables, and fields; aware of type annotations and unpacked variables
18
28
  - **Dependency graph** — builds directed edges: `read`, `write`, `call`, `inherit`, `decorate`
19
- - **Entry point detection** — 10 built-in rules (main, FastAPI, Flask, Django, Click, Typer, Celery, pytest, plus package and test entries) and custom rules
20
- - **Multi-language architecture** — language adapter abstraction layer, laying the foundation for future multi-language support
29
+ - **Entry point detection** — 17 built-in rules covering Python frameworks (FastAPI, Flask, Django, Click, Typer, Celery, pytest) and Rust conventions (main, async runtimes, WASM, proc macros, FFI, tests, pub API) plus custom rules
30
+ - **Configurable entry templates** — add custom entry rules via `ast_pattern` prefixes including `function_call:`, `function_def:`, `decorator:`, `file_match:`, `visibility:pub` (Rust), `trait_impl:` (Rust), `macro_def:` (Rust), and more
31
+ - **`--public-as-entry` flag** — treat all public items (Rust `pub`) as entry points for library-crate analysis
21
32
  - **Warning detection** — 11 warning types including circular references, unused imports, write-only variables, and more
22
33
  - **Python API + CLI** — integrate into any Tool, CI pipeline, or let agents self-analyze and self-clean
23
34
 
@@ -29,6 +40,12 @@ pip install graphlint
29
40
 
30
41
  **Requirements:** Python >= 3.9
31
42
 
43
+ For Rust support (`.rs` files), install the optional `tree-sitter` dependencies:
44
+
45
+ ```bash
46
+ pip install graphlint[rust]
47
+ ```
48
+
32
49
  ## Quick Start
33
50
 
34
51
  ### Agent Integration
@@ -65,6 +82,9 @@ graphlint query -g 1 --detail full
65
82
  # Exit non-zero when dead code or circular refs found (for CI)
66
83
  graphlint query --json --fail-on dead_code,circular_ref
67
84
 
85
+ # Treat all public items as entry points (library-crate mode)
86
+ graphlint query --public-as-entry
87
+
68
88
  # Rebuild index
69
89
  graphlint build --force
70
90
 
@@ -162,7 +182,10 @@ Full documentation is available in the [docs/](https://github.com/AngelosZou/gra
162
182
 
163
183
  ## Limitations
164
184
 
165
- - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
185
+ - **Static analysis only** — graphlint performs static analysis and cannot detect runtime linkage such as `getattr`, `importlib`, or dynamic dispatch patterns, which may result in false positives. This primarily affects Python; Rust's static dispatch model produces fewer false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
186
+ - **Python dynamic imports** — due to Python's dynamic import mechanisms (`importlib`, `getattr`, metaclasses, etc.), the default entry templates may produce false positives in codebases that rely heavily on runtime dispatch. Users should tune the `entry_rules` configuration to match their project's conventions.
187
+ - **Rust macro expansion** — tree-sitter parses unexpanded source; procedural macros and `macro_rules!` bodies appear as opaque token trees. Some macro-generated call paths may be missed. `#[derive]` attributes are partially recognized via implicit `inherit` edges.
188
+ - **`--public-as-entry` scope** — this flag only applies to languages with `public` visibility declarations (Rust `pub`). It has no effect on Python files. Toggling this flag triggers a full re-index. For long-term library-crate analysis, prefer enabling the `rust_pub_api` entry rule via `graphlint config` to persist the setting.
166
189
  - **Large codebase build time** — on a large codebase with 700+ `.py` files, 1,000+ classes, and 14,000+ functions, a full rebuild takes approximately 200 seconds (actual performance depends on hardware). Small projects (~60 files) complete in ~1 second. **Best practice:** run `query` before making changes to plan your work, and avoid invoking `query` during refactoring to prevent unnecessary index rebuilds.
167
190
 
168
191
  ## License
@@ -1,6 +1,6 @@
1
1
  # Architecture Overview
2
2
 
3
- graphlint is positioned as a dead code detection tool for AI-generated codebases. It analyzes the dependency graph to find components unreachable from all entry points (dead code), helping agents clean redundant code and reduce context pollution and attention dilution.
3
+ graphlint is positioned as a dead code detection tool for AI-generated codebases (Python, Rust, and more in the future). It analyzes the dependency graph to find components unreachable from all entry points (dead code), helping agents clean redundant code and reduce context pollution and attention dilution.
4
4
 
5
5
  ## Overall Architecture
6
6
 
@@ -97,8 +97,8 @@ AST Parse (ast.parse / other language parser)
97
97
  │
98
98
  ▼
99
99
  ASTVisitor Traversal (single pass)
100
- ├── Extract nodes (classes/functions/methods/variables/fields)
101
- ├── Parse import statements
100
+ ├── Extract nodes (classes/functions/methods/structs/enums/traits/impls/variables/fields)
101
+ ├── Parse import statements (Python) / use declarations (Rust)
102
102
  ├── Collect name usage
103
103
  └── Collect structured references (ReferenceInfo — read/write/call/inherit/decorate)
104
104
  │
@@ -45,6 +45,7 @@ graphlint query -j # JSON format output
45
45
  | `--file-limit` | — | int | `10` | Max files shown in graph detail (0=unlimited) |
46
46
  | `--node-limit` | — | int | `30` | Max nodes shown in graph detail (0=unlimited) |
47
47
  | `--no-scan` | — | flag | `false` | Skip auto-scan/build, query existing index only |
48
+ | `--public-as-entry` | — | flag | `false` | Treat public methods (Rust `pub`) as execution entry points — see [Entry Detection](../guide/entry-detection.md#-public-as-entry-flag) |
48
49
  | `--fail-on` | — | string | — | Exit non-zero if matching warning types found (comma-separated) |
49
50
 
50
51
  ### Examples
@@ -16,10 +16,17 @@ graphlint's configuration file is located at `.graphlint/config.json` in the pro
16
16
  {"name": "typer_app", "ast_pattern": "class_instantiation:typer.Typer | decorator:*.command", "file_pattern": "**/*.py", "enabled": true},
17
17
  {"name": "celery_app", "ast_pattern": "class_instantiation:Celery | class_instantiation:celery.Celery", "file_pattern": "**/*.py", "enabled": true},
18
18
  {"name": "pytest_plugin", "ast_pattern": "function_def:pytest_addoption | decorator:pytest.fixture", "file_pattern": "**/conftest.py", "enabled": true},
19
- {"name": "pytest_test", "ast_pattern": "test_file", "file_pattern": "**/*.py", "enabled": true, "no_propagate": true}
19
+ {"name": "pytest_test", "ast_pattern": "test_file", "file_pattern": "**/*.py", "enabled": true, "no_propagate": true},
20
+ {"name": "rust_main", "ast_pattern": "function_def:main", "file_pattern": "**/*.rs", "enabled": true},
21
+ {"name": "rust_async_main", "ast_pattern": "decorator:tokio::main | decorator:actix_rt::main | decorator:actix_web::main | decorator:async_std::main | decorator:rocket::main | decorator:rocket::launch | decorator:main", "file_pattern": "**/*.rs", "enabled": true},
22
+ {"name": "rust_wasm_entry", "ast_pattern": "decorator:wasm_bindgen", "file_pattern": "**/*.rs", "enabled": true},
23
+ {"name": "rust_proc_macro", "ast_pattern": "decorator:proc_macro | decorator:proc_macro_derive | decorator:proc_macro_attribute", "file_pattern": "**/*.rs", "enabled": true},
24
+ {"name": "rust_ffi_export", "ast_pattern": "decorator:no_mangle | decorator:export_name", "file_pattern": "**/*.rs", "enabled": true},
25
+ {"name": "rust_test", "ast_pattern": "test_file", "file_pattern": "**/*.rs", "enabled": true, "no_propagate": true},
26
+ {"name": "rust_pub_api", "ast_pattern": "visibility:pub", "file_pattern": "**/*.rs", "enabled": false}
20
27
  ],
21
28
  "exclude_patterns": {
22
- "always_exclude": ["__pycache__/", ".mypy_cache/", ".pytest_cache/", ".tox/", ".venv/", "venv/", "env/", "virtualenv/", ".env/", "node_modules/", ".git/", ".svn/", ".hg/", ".idea/", ".vscode/", ".vs/", ".graphlint/", "build/", "dist/", "*.egg-info/", "*.pyc", "*.pyo"],
29
+ "always_exclude": ["__pycache__/", ".mypy_cache/", ".pytest_cache/", ".tox/", ".venv/", "venv/", "env/", "virtualenv/", ".env/", "node_modules/", ".git/", ".svn/", ".hg/", ".idea/", ".vscode/", ".vs/", ".graphlint/", "build/", "dist/", "target/", ".cargo/", "*.egg-info/", "*.pyc", "*.pyo"],
23
30
  "user_exclude": []
24
31
  },
25
32
  "lang": "system",
@@ -36,7 +43,7 @@ graphlint's configuration file is located at `.graphlint/config.json` in the pro
36
43
  "test_patterns": {
37
44
  "config_files": ["conftest.py"],
38
45
  "dir_patterns": ["tests/", "test/", "__tests__/"],
39
- "file_patterns": ["test_*.py", "*_test.py"],
46
+ "file_patterns": ["test_*.py", "*_test.py", "test_*.rs", "*_test.rs"],
40
47
  "function_patterns": ["test_*"]
41
48
  },
42
49
  "version": 1
@@ -58,19 +65,36 @@ Defines how code entry points are auto-detected. Each rule contains:
58
65
  | `enabled` | `boolean` | Whether the rule is enabled |
59
66
  | `no_propagate` | `boolean` | Entry does not propagate reachability (default `false`, e.g. pytest test rules set to `true`) |
60
67
 
68
+ **Note:** The `rust_pub_api` rule (pattern `visibility:pub`, Rust only) is **disabled by default** (`"enabled": false`). Use `--public-as-entry` at query time, or manually set `"enabled": true` in the config for persistent library-crate analysis.
69
+
70
+ ### --public-as-entry Flag
71
+
72
+ The `--public-as-entry` CLI flag (or `public_as_entry=True` API parameter) treats all public items as execution entry points:
73
+
74
+ ```bash
75
+ graphlint query --public-as-entry
76
+ ```
77
+
78
+ - **Scope:** Only affects languages with `public` visibility declarations (Rust `pub`). Has no effect on Python files.
79
+ - **Re-indexing:** Toggling the flag triggers a full rebuild (the flag value is stored in the scan stamp). For long-term use, prefer enabling the `rust_pub_api` config rule or adding custom `visibility:pub` entry rules to avoid repeated rebuilds.
80
+ - **Config interaction:** Independent of the `rust_pub_api` config rule — the two mechanisms are orthogonal and do not interfere.
81
+
61
82
  #### AST Pattern Prefixes
62
83
 
63
84
  All rules use the same prefix pattern syntax, supporting OR combinations with ` | `:
64
85
 
65
- | Prefix | Match Target | Example |
66
- |--------|-------------|---------|
67
- | `function_call:` | Function call | `"function_call:my_entry"` |
68
- | `function_def:` | Function definition name (supports glob) | `"function_def:run_*"` |
69
- | `decorator:` | Decorator | `"decorator:app.route"` |
70
- | `class_instantiation:` | Class instantiation | `"class_instantiation:MyApp"` |
71
- | `file_match:` | Filename match | `"file_match:**/main.py"` |
72
- | `if_name_main` | `if __name__ == '__main__'` check | `"if_name_main"` |
73
- | `test_file` | Test file detection (uses `test_patterns` config) | `"test_file"` |
86
+ | Prefix | Match Target | Example | Languages |
87
+ |--------|-------------|---------|-----------|
88
+ | `function_call:` | Function call | `"function_call:my_entry"` | Python |
89
+ | `function_def:` | Function definition name (supports glob) | `"function_def:run_*"` | Python, Rust |
90
+ | `decorator:` | Decorator or attribute macro (`#[...]`) | `"decorator:app.route"` / `"decorator:tokio::main"` | Python, Rust |
91
+ | `class_instantiation:` | Class instantiation | `"class_instantiation:MyApp"` | Python |
92
+ | `file_match:` | Filename match | `"file_match:**/main.py"` | Python, Rust |
93
+ | `if_name_main` | `if __name__ == '__main__'` check | `"if_name_main"` | Python |
94
+ | `test_file` | Test file detection (uses `test_patterns` config) | `"test_file"` | Python, Rust |
95
+ | `visibility:pub` | Items with `pub` visibility modifier | `"visibility:pub"` | Rust |
96
+ | `trait_impl:` | Trait implementation block | `"trait_impl:Default"` | Rust |
97
+ | `macro_def:` | `macro_rules!` definition | `"macro_def:my_macro"` | Rust |
74
98
 
75
99
  ### exclude_patterns — Exclude Patterns
76
100
 
@@ -1,9 +1,11 @@
1
1
  # Entry Point Detection Reference
2
2
 
3
- graphlint has 10 built-in entry point detection rules and supports custom rule extension. On first `graphlint build`, these rules are written as a template into `.graphlint/config.json`; thereafter the config file is the single source of truth — you may add, remove, or modify rules via `graphlint config` or by editing the file directly.
3
+ graphlint has 17 built-in entry point detection rules (10 for Python, 7 for Rust) and supports custom rule extension. On first `graphlint build`, these rules are written as a template into `.graphlint/config.json`; thereafter the config file is the single source of truth — you may add, remove, or modify rules via `graphlint config` or by editing the file directly.
4
4
 
5
5
  ## Built-in Rules
6
6
 
7
+ ### Python Rules
8
+
7
9
  ### 1. python_main
8
10
 
9
11
  Detects the standard Python entry point `if __name__ == '__main__':`.
@@ -167,6 +169,117 @@ Detects Pytest test cases as entry points.
167
169
  pass
168
170
  ```
169
171
 
172
+ ### Rust Rules
173
+
174
+ ### 11. rust_main
175
+
176
+ Detects the Rust binary crate entry point `fn main()`.
177
+
178
+ - **Match Pattern**: `function_def:main`
179
+ - **Match Files**: `**/*.rs`
180
+ - **Example**:
181
+ ```rust
182
+ fn main() {
183
+ println!("Hello, world!");
184
+ }
185
+ ```
186
+
187
+ ### 12. rust_async_main
188
+
189
+ Detects async runtime `#[...]` attributes that decorate `main`.
190
+
191
+ - **Match Pattern**: `decorator:tokio::main | decorator:actix_rt::main | decorator:actix_web::main | decorator:async_std::main | decorator:rocket::main | decorator:rocket::launch | decorator:main`
192
+ - **Match Files**: `**/*.rs`
193
+ - **Note**: Rust attribute macros (`#[...]`) are compile-time and do **not** create `decorate` edges in the dependency graph (unlike Python decorators).
194
+ - **Example**:
195
+ ```rust
196
+ #[tokio::main]
197
+ async fn main() {
198
+ // ...
199
+ }
200
+ ```
201
+
202
+ ### 13. rust_wasm_entry
203
+
204
+ Detects WebAssembly exports.
205
+
206
+ - **Match Pattern**: `decorator:wasm_bindgen`
207
+ - **Match Files**: `**/*.rs`
208
+ - **Example**:
209
+ ```rust
210
+ use wasm_bindgen::prelude::*;
211
+
212
+ #[wasm_bindgen]
213
+ pub fn greet(name: &str) {
214
+ // ...
215
+ }
216
+ ```
217
+
218
+ ### 14. rust_proc_macro
219
+
220
+ Detects Rust procedural macro entry points called by the compiler.
221
+
222
+ - **Match Pattern**: `decorator:proc_macro | decorator:proc_macro_derive | decorator:proc_macro_attribute`
223
+ - **Match Files**: `**/*.rs`
224
+ - **Example**:
225
+ ```rust
226
+ #[proc_macro]
227
+ pub fn my_macro(input: TokenStream) -> TokenStream {
228
+ // ...
229
+ }
230
+ ```
231
+
232
+ ### 15. rust_ffi_export
233
+
234
+ Detects FFI exports via `#[no_mangle]` or `#[export_name]`.
235
+
236
+ - **Match Pattern**: `decorator:no_mangle | decorator:export_name`
237
+ - **Match Files**: `**/*.rs`
238
+ - **Example**:
239
+ ```rust
240
+ #[no_mangle]
241
+ pub extern "C" fn my_export() {
242
+ // ...
243
+ }
244
+ ```
245
+
246
+ ### 16. rust_test
247
+
248
+ Detects Rust test files and `#[test]` functions.
249
+
250
+ - **Match Pattern**: `test_file`
251
+ - **Match Files**: `**/*.rs`
252
+ - **Note**: Test entry points do not propagate reachability to non-test code under test.
253
+ - **Example**:
254
+ ```rust
255
+ #[test]
256
+ fn test_addition() {
257
+ assert_eq!(2 + 2, 4);
258
+ }
259
+ ```
260
+
261
+ ### 17. rust_pub_api
262
+
263
+ Treats all `pub` items in Rust library crates as entry points.
264
+
265
+ - **Match Pattern**: `visibility:pub`
266
+ - **Match Files**: `**/*.rs`
267
+ - **Note**: Disabled by default (`"enabled": false`). Use `--public-as-entry` to activate it at query time, or enable the rule in config for persistent library-crate analysis.
268
+
269
+ ## `--public-as-entry` Flag
270
+
271
+ The `--public-as-entry` flag provides an alternative way to treat public items as entry points without modifying the config:
272
+
273
+ ```bash
274
+ graphlint query --public-as-entry
275
+ ```
276
+
277
+ This flag:
278
+ - Only applies to languages with `public` visibility declarations (Rust `pub`); has no effect on Python files.
279
+ - Toggles independently from the `rust_pub_api` config entry rule — the two are orthogonal mechanisms.
280
+ - **Triggers a full re-index** when switched on or off (detected via the scan stamp).
281
+ - For long-term library-crate analysis, prefer enabling `rust_pub_api` in `.graphlint/config.json` to persist the setting and avoid repeated rebuilds.
282
+
170
283
  ## Custom Rules
171
284
 
172
285
  Add custom entry detection rules via the `entry_rules` configuration.
@@ -175,15 +288,18 @@ Add custom entry detection rules via the `entry_rules` configuration.
175
288
 
176
289
  All rules (built-in and custom) use the same prefix syntax, supporting OR combinations with ` | `.
177
290
 
178
- | Prefix | Description | Example |
179
- |--------|-------------|---------|
180
- | `function_call:<name>` | Match a function call by name | `"function_call:start_app"` |
181
- | `function_def:<pattern>` | Match a function definition by name (supports glob) | `"function_def:run_*"` |
182
- | `decorator:<name>` | Match a decorator by name | `"decorator:app.route"` |
183
- | `class_instantiation:<name>` | Match a class instantiation by name | `"class_instantiation:MyApp"` |
184
- | `file_match:<pattern>` | Match a filename pattern | `"file_match:**/startup.py"` |
185
- | `if_name_main` | Match `if __name__ == '__main__'` | `"if_name_main"` |
186
- | `test_file` | Match test files (uses `test_patterns` config) | `"test_file"` |
291
+ | Prefix | Description | Example | Applicable |
292
+ |--------|-------------|---------|------------|
293
+ | `function_call:<name>` | Match a function call by name | `"function_call:start_app"` | Python |
294
+ | `function_def:<pattern>` | Match a function definition by name (supports glob) | `"function_def:run_*"` | Python, Rust |
295
+ | `decorator:<name>` | Match a decorator (Python) or attribute macro (Rust `#[...]`) by name | `"decorator:app.route"` / `"decorator:tokio::main"` | Python, Rust |
296
+ | `class_instantiation:<name>` | Match a class instantiation by name | `"class_instantiation:MyApp"` | Python |
297
+ | `file_match:<pattern>` | Match a filename pattern | `"file_match:**/startup.py"` | Python, Rust |
298
+ | `if_name_main` | Match `if __name__ == '__main__'` | `"if_name_main"` | Python |
299
+ | `test_file` | Match test files (uses `test_patterns` config) | `"test_file"` | Python, Rust |
300
+ | `visibility:pub` | Match items with `pub` visibility modifier | `"visibility:pub"` | Rust |
301
+ | `trait_impl:<pattern>` | Match `impl Trait for Type` blocks | `"trait_impl:Default"` | Rust |
302
+ | `macro_def:<pattern>` | Match `macro_rules!` definitions | `"macro_def:my_macro"` | Rust |
187
303
 
188
304
  ### Custom Rule Examples
189
305
 
@@ -10,7 +10,13 @@ graphlint provides a **Python API** (for integration into any Tool development)
10
10
  pip install graphlint
11
11
  ```
12
12
 
13
- Requirements: Python >= 3.9, zero third-party dependencies.
13
+ Requirements: Python >= 3.9
14
+
15
+ For Rust support (`.rs` files), install the optional `tree-sitter` dependencies:
16
+
17
+ ```bash
18
+ pip install graphlint[rust]
19
+ ```
14
20
 
15
21
  ## Agent Integration
16
22
 
@@ -4,20 +4,31 @@
4
4
  [![Python](https://img.shields.io/pypi/pyversions/graphlint)](https://pypi.org/project/graphlint/)
5
5
  [![License](https://img.shields.io/pypi/l/graphlint)](https://github.com/AngelosZou/graphlint/blob/main/LICENSE)
6
6
 
7
- [![en](https://img.shields.io/badge/lang-en-red.svg)](../../README.md)
8
- [![zh](https://img.shields.io/badge/lang-zh--CN-blue.svg)](README.md)
7
+ [English](../../README.md) | [简体中文](README.md)
9
8
 
10
- **面向 AI 生成 Python 代码库的死代码检测工具。**
9
+ **面向 AI 生成代码库的死代码检测工具。**
11
10
 
12
11
  AI Agent 在快速生成代码的同时,也会留下大量冗余和死代码。这些代码污染了 LLM 的上下文窗口,稀释了注意力。Graphlint 通过分析代码库的依赖关系图,识别入口点并**检测死代码**(从任何入口点均不可达的组件),让 Agent 能够自行清理,保持代码库的简洁高效。
13
12
 
13
+ ## 支持的语言
14
+
15
+ | 语言 | 状态 | 解析器 | 特性 |
16
+ |------|------|--------|------|
17
+ | **Python** (`.py`) | 内置 | `ast`(标准库) | 装饰器、类型注解、动态导入、框架感知入口检测 |
18
+ | **Rust** (`.rs`) | 内置(可选依赖) | `tree-sitter` | 属性宏、Trait、`pub` 可见性、`macro_rules!` |
19
+
20
+ 安装含 Rust 支持的版本:`pip install graphlint[rust]`(额外引入 `tree-sitter` 和 `tree-sitter-rust`)。
21
+
14
22
  ## 特性
15
23
 
16
24
  - **死代码检测** — 通过图遍历找出所有入口点不可达的组件
17
- - **AST 解析** — 提取类、函数、方法、变量、字段;感知类型声明,自动处理循环解包等变量绑定
25
+ - **多语言支持** — Python 和 Rust 后端,通过语言适配器抽象层实现;Python 使用标准库 `ast`,Rust 使用 `tree-sitter`
26
+ - **语言专有特性感知** — Python 装饰器、Rust 属性宏(`#[tokio::main]`、`#[test]`)、Trait 实现、`pub` 可见性等
27
+ - **AST/CST 解析** — 提取函数、方法、结构体、枚举、Trait、实现块、宏、变量、字段;感知类型声明,自动处理循环解包等变量绑定
18
28
  - **依赖图构建** — 有向边:`read`、`write`、`call`、`inherit`、`decorate`
19
- - **入口点检测** — 10 种内置规则(main、FastAPI、Flask、Django、Click、Typer、Celery、pytest、包入口和测试入口)及自定义规则
20
- - **多语言架构** — 语言适配器抽象层,为未来多语言支持提供基础
29
+ - **入口点检测** — 17 种内置规则,覆盖 Python 框架(FastAPI、Flask、Django、Click、Typer、Celery、pytest)和 Rust 惯例(main、异步运行时、WASM、proc 宏、FFI、测试、pub API)及自定义规则
30
+ - **可配置入口模板** — 通过 `ast_pattern` 前缀添加自定义入口规则,包括 `function_call:`、`function_def:`、`decorator:`、`file_match:`、`visibility:pub`(Rust)、`trait_impl:`(Rust)、`macro_def:`(Rust)等
31
+ - **`--public-as-entry` 标志** — 将所有公开项(Rust `pub`)视为入口点,用于库代码分析
21
32
  - **警告检测** — 11 种警告类型:循环引用、未使用 import、只写变量、死代码等
22
33
  - **Python API + CLI** — 可集成到任何 Tool 开发、CI 流程,或直接供 Agent 自行分析和清理
23
34
 
@@ -29,6 +40,12 @@ pip install graphlint
29
40
 
30
41
  **要求:** Python >= 3.9
31
42
 
43
+ 如需支持 Rust(`.rs` 文件),安装可选的 `tree-sitter` 依赖:
44
+
45
+ ```bash
46
+ pip install graphlint[rust]
47
+ ```
48
+
32
49
  ## 快速开始
33
50
 
34
51
  ### Agent 集成
@@ -65,6 +82,9 @@ graphlint query -g 1 --detail full
65
82
  # 检测到死代码或循环引用时返回非零退出码(用于 CI)
66
83
  graphlint query --json --fail-on dead_code,circular_ref
67
84
 
85
+ # 将所有公开项视为入口点(库模式分析)
86
+ graphlint query --public-as-entry
87
+
68
88
  # 重建索引
69
89
  graphlint build --force
70
90
 
@@ -162,7 +182,10 @@ Graphlint 将配置存储在分析目录下的 `.graphlint/config.json` 中。
162
182
 
163
183
  ## 局限性
164
184
 
165
- - **纯静态分析** — graphlint 基于静态分析执行,无法检测 `getattr`、`importlib` 等运行时动态链接或动态分发模式,这可能会导致假阳性。**缓解方案:** 根据代码库的实际约定添加自定义入口规则。例如,graphlint 自身的代码库使用了 `function_def:_detect_*` 和 `function_def:visit_*` 两种 ast_pattern,防止通过 `getattr` 发现的函数被误报为死代码。
185
+ - **纯静态分析** — graphlint 基于静态分析执行,无法检测 `getattr`、`importlib` 等运行时动态链接或动态分发模式,这可能会导致假阳性。此问题主要影响 Python;Rust 的静态分发模型产生的假阳性较少。**缓解方案:** 根据代码库的实际约定添加自定义入口规则。例如,graphlint 自身的代码库使用了 `function_def:_detect_*` 和 `function_def:visit_*` 两种 ast_pattern,防止通过 `getattr` 发现的函数被误报为死代码。
186
+ - **Python 动态导入** — 由于 Python 的动态导入机制(`importlib`、`getattr`、元类等),默认入口模板在重度依赖运行时调度的代码库中可能产生假阳性。用户应根据项目约定调整 `entry_rules` 配置。
187
+ - **Rust 宏展开** — tree-sitter 解析未展开的源代码;过程宏和 `macro_rules!` 体显示为不透明标记树。部分宏生成的调用路径可能遗漏。`#[derive]` 属性通过隐式 `inherit` 边部分识别。
188
+ - **`--public-as-entry` 适用范围** — 此标志仅适用于具有 `public` 可见性声明的语言(Rust `pub`),对 Python 文件无效。开关此标志会触发全量重新索引。对于长期库代码分析,建议通过 `graphlint config` 启用 `rust_pub_api` 入口规则以持久化设置。
166
189
  - **大规模代码库构建耗时** — 在包含 700+ 个 Python 文件、1,000+ 个类定义和 14,000+ 个函数的大规模代码库上,完整重构需要约 200 秒(实际时间与设备性能有关)。小型项目(~60 个文件)约 1 秒完成。**最佳实践:** 在改动前运行 `query` 了解现状并规划方案,改动期间避免执行 `query` 以防止不必要地触发索引重构。
167
190
 
168
191
  ## 许可证