pyvbaanalysis 1.0.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 (159) hide show
  1. pyvbaanalysis-1.0.0/.gitattributes +3 -0
  2. pyvbaanalysis-1.0.0/.github/dependabot.yml +13 -0
  3. pyvbaanalysis-1.0.0/.github/workflows/ci.yml +52 -0
  4. pyvbaanalysis-1.0.0/.github/workflows/publish.yml +53 -0
  5. pyvbaanalysis-1.0.0/.gitignore +36 -0
  6. pyvbaanalysis-1.0.0/CHANGELOG.md +47 -0
  7. pyvbaanalysis-1.0.0/CONTRIBUTING.md +105 -0
  8. pyvbaanalysis-1.0.0/LICENSE +21 -0
  9. pyvbaanalysis-1.0.0/PKG-INFO +120 -0
  10. pyvbaanalysis-1.0.0/README.md +91 -0
  11. pyvbaanalysis-1.0.0/docs/api-reference.md +86 -0
  12. pyvbaanalysis-1.0.0/docs/diagnostics-catalogue.md +152 -0
  13. pyvbaanalysis-1.0.0/docs/usage.md +225 -0
  14. pyvbaanalysis-1.0.0/pyproject.toml +80 -0
  15. pyvbaanalysis-1.0.0/pyvbaanalysis/__init__.py +59 -0
  16. pyvbaanalysis-1.0.0/pyvbaanalysis/__main__.py +8 -0
  17. pyvbaanalysis-1.0.0/pyvbaanalysis/call/__init__.py +23 -0
  18. pyvbaanalysis-1.0.0/pyvbaanalysis/call/call_context.py +296 -0
  19. pyvbaanalysis-1.0.0/pyvbaanalysis/cli.py +352 -0
  20. pyvbaanalysis-1.0.0/pyvbaanalysis/completion/__init__.py +49 -0
  21. pyvbaanalysis-1.0.0/pyvbaanalysis/completion/cursor_context.py +26 -0
  22. pyvbaanalysis-1.0.0/pyvbaanalysis/completion/event_handlers.py +82 -0
  23. pyvbaanalysis-1.0.0/pyvbaanalysis/completion/member_access.py +1097 -0
  24. pyvbaanalysis-1.0.0/pyvbaanalysis/completion/type_completion.py +228 -0
  25. pyvbaanalysis-1.0.0/pyvbaanalysis/conditional/__init__.py +41 -0
  26. pyvbaanalysis-1.0.0/pyvbaanalysis/conditional/conditional_compilation.py +531 -0
  27. pyvbaanalysis-1.0.0/pyvbaanalysis/constants/__init__.py +21 -0
  28. pyvbaanalysis-1.0.0/pyvbaanalysis/constants/integer_constant_expression.py +226 -0
  29. pyvbaanalysis-1.0.0/pyvbaanalysis/data/diagnostic_influence_audit.json +1786 -0
  30. pyvbaanalysis-1.0.0/pyvbaanalysis/data/event_definitions.json +1 -0
  31. pyvbaanalysis-1.0.0/pyvbaanalysis/data/excel_host_model.json +1 -0
  32. pyvbaanalysis-1.0.0/pyvbaanalysis/data/manifest.json +257 -0
  33. pyvbaanalysis-1.0.0/pyvbaanalysis/data/rule_metadata.json +1287 -0
  34. pyvbaanalysis-1.0.0/pyvbaanalysis/data/vba_runtime_tables.json +1 -0
  35. pyvbaanalysis-1.0.0/pyvbaanalysis/data/vbe_oracle_cases.json +4578 -0
  36. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/__init__.py +59 -0
  37. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/analyze_module.py +177 -0
  38. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/argument_inference.py +706 -0
  39. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/call_extraction.py +380 -0
  40. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/callable_signatures.py +443 -0
  41. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/const_expr.py +134 -0
  42. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/context.py +130 -0
  43. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/dataflow.py +242 -0
  44. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/exprwalk.py +110 -0
  45. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/model.py +108 -0
  46. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/registry.py +263 -0
  47. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rule_metadata.py +243 -0
  48. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/__init__.py +2 -0
  49. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/argument_shape.py +204 -0
  50. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/argument_types.py +80 -0
  51. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/arrays.py +1000 -0
  52. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/assignments.py +650 -0
  53. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/binary_operand_scalar.py +75 -0
  54. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/call_arity.py +114 -0
  55. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/control_flow.py +651 -0
  56. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/declarations.py +1642 -0
  57. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/duplicates.py +311 -0
  58. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/expressions.py +679 -0
  59. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/lexical.py +185 -0
  60. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/module_kind.py +389 -0
  61. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/numeric_literals.py +49 -0
  62. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/object_state.py +327 -0
  63. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/parameter_defaults.py +92 -0
  64. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/runtime_values.py +382 -0
  65. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/shared.py +441 -0
  66. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/type_of_is.py +312 -0
  67. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/rules/undeclared.py +503 -0
  68. pyvbaanalysis-1.0.0/pyvbaanalysis/diagnostics/walker.py +347 -0
  69. pyvbaanalysis-1.0.0/pyvbaanalysis/evidence.py +111 -0
  70. pyvbaanalysis-1.0.0/pyvbaanalysis/flow/__init__.py +21 -0
  71. pyvbaanalysis-1.0.0/pyvbaanalysis/flow/procedure_labels.py +274 -0
  72. pyvbaanalysis-1.0.0/pyvbaanalysis/flow/procedure_unstructured.py +65 -0
  73. pyvbaanalysis-1.0.0/pyvbaanalysis/host/__init__.py +41 -0
  74. pyvbaanalysis-1.0.0/pyvbaanalysis/host/host_model.py +209 -0
  75. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/__init__.py +43 -0
  76. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/keyword_table.py +141 -0
  77. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/stripped_lines.py +41 -0
  78. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/token_helpers.py +115 -0
  79. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/token_kinds.py +113 -0
  80. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/tokenize.py +413 -0
  81. pyvbaanalysis-1.0.0/pyvbaanalysis/lexer/trivia.py +65 -0
  82. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/__init__.py +22 -0
  83. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/fixed_length_string.py +58 -0
  84. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/nodes.py +721 -0
  85. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/parse_expression.py +621 -0
  86. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/parse_module.py +1472 -0
  87. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/parser_state.py +110 -0
  88. pyvbaanalysis-1.0.0/pyvbaanalysis/parser/type_declaration_suffix.py +29 -0
  89. pyvbaanalysis-1.0.0/pyvbaanalysis/project.py +146 -0
  90. pyvbaanalysis-1.0.0/pyvbaanalysis/py.typed +0 -0
  91. pyvbaanalysis-1.0.0/pyvbaanalysis/reader/__init__.py +49 -0
  92. pyvbaanalysis-1.0.0/pyvbaanalysis/reader/loose_file.py +104 -0
  93. pyvbaanalysis-1.0.0/pyvbaanalysis/reader/vbe_module.py +137 -0
  94. pyvbaanalysis-1.0.0/pyvbaanalysis/reader/workbook.py +104 -0
  95. pyvbaanalysis-1.0.0/pyvbaanalysis/runtime/__init__.py +29 -0
  96. pyvbaanalysis-1.0.0/pyvbaanalysis/runtime/vba_runtime.py +314 -0
  97. pyvbaanalysis-1.0.0/pyvbaanalysis/symbols/__init__.py +60 -0
  98. pyvbaanalysis-1.0.0/pyvbaanalysis/symbols/build_module_symbols.py +379 -0
  99. pyvbaanalysis-1.0.0/pyvbaanalysis/symbols/name_resolution.py +252 -0
  100. pyvbaanalysis-1.0.0/pyvbaanalysis/symbols/project_index.py +942 -0
  101. pyvbaanalysis-1.0.0/pyvbaanalysis/symbols/symbol_model.py +371 -0
  102. pyvbaanalysis-1.0.0/pyvbaanalysis/types/__init__.py +17 -0
  103. pyvbaanalysis-1.0.0/pyvbaanalysis/types/type_inference.py +278 -0
  104. pyvbaanalysis-1.0.0/pyvbaanalysis/types/type_names.py +105 -0
  105. pyvbaanalysis-1.0.0/tests/conftest.py +6 -0
  106. pyvbaanalysis-1.0.0/tests/oracle_support.py +119 -0
  107. pyvbaanalysis-1.0.0/tests/test_cli.py +222 -0
  108. pyvbaanalysis-1.0.0/tests/test_conditional_compilation.py +172 -0
  109. pyvbaanalysis-1.0.0/tests/test_dataflow.py +119 -0
  110. pyvbaanalysis-1.0.0/tests/test_diagnostics_engine.py +179 -0
  111. pyvbaanalysis-1.0.0/tests/test_evidence.py +33 -0
  112. pyvbaanalysis-1.0.0/tests/test_host_model.py +56 -0
  113. pyvbaanalysis-1.0.0/tests/test_integer_constant_expression.py +122 -0
  114. pyvbaanalysis-1.0.0/tests/test_lexer.py +101 -0
  115. pyvbaanalysis-1.0.0/tests/test_parser.py +292 -0
  116. pyvbaanalysis-1.0.0/tests/test_project.py +104 -0
  117. pyvbaanalysis-1.0.0/tests/test_project_index.py +238 -0
  118. pyvbaanalysis-1.0.0/tests/test_public_api.py +63 -0
  119. pyvbaanalysis-1.0.0/tests/test_reader.py +264 -0
  120. pyvbaanalysis-1.0.0/tests/test_rule_metadata.py +97 -0
  121. pyvbaanalysis-1.0.0/tests/test_rules_ambiguous_enum_member.py +171 -0
  122. pyvbaanalysis-1.0.0/tests/test_rules_argument_shape.py +58 -0
  123. pyvbaanalysis-1.0.0/tests/test_rules_argument_types.py +63 -0
  124. pyvbaanalysis-1.0.0/tests/test_rules_arrays.py +202 -0
  125. pyvbaanalysis-1.0.0/tests/test_rules_assignments.py +51 -0
  126. pyvbaanalysis-1.0.0/tests/test_rules_assignments_types.py +173 -0
  127. pyvbaanalysis-1.0.0/tests/test_rules_binary_operand_scalar.py +41 -0
  128. pyvbaanalysis-1.0.0/tests/test_rules_call_arity.py +60 -0
  129. pyvbaanalysis-1.0.0/tests/test_rules_control_flow.py +141 -0
  130. pyvbaanalysis-1.0.0/tests/test_rules_declarations.py +192 -0
  131. pyvbaanalysis-1.0.0/tests/test_rules_division_by_zero.py +55 -0
  132. pyvbaanalysis-1.0.0/tests/test_rules_duplicates.py +76 -0
  133. pyvbaanalysis-1.0.0/tests/test_rules_event_handler_module_scope.py +96 -0
  134. pyvbaanalysis-1.0.0/tests/test_rules_expression_calls.py +78 -0
  135. pyvbaanalysis-1.0.0/tests/test_rules_expressions.py +39 -0
  136. pyvbaanalysis-1.0.0/tests/test_rules_invalid_as_type_names.py +231 -0
  137. pyvbaanalysis-1.0.0/tests/test_rules_is_operator_non_object.py +70 -0
  138. pyvbaanalysis-1.0.0/tests/test_rules_lexical.py +62 -0
  139. pyvbaanalysis-1.0.0/tests/test_rules_member_not_found.py +173 -0
  140. pyvbaanalysis-1.0.0/tests/test_rules_module_kind.py +97 -0
  141. pyvbaanalysis-1.0.0/tests/test_rules_numeric_literals.py +34 -0
  142. pyvbaanalysis-1.0.0/tests/test_rules_object_state.py +98 -0
  143. pyvbaanalysis-1.0.0/tests/test_rules_parameter_defaults.py +58 -0
  144. pyvbaanalysis-1.0.0/tests/test_rules_property_setter_value_parameters.py +116 -0
  145. pyvbaanalysis-1.0.0/tests/test_rules_runtime_values.py +62 -0
  146. pyvbaanalysis-1.0.0/tests/test_rules_scalar_and_foreach.py +103 -0
  147. pyvbaanalysis-1.0.0/tests/test_rules_type_of_is.py +36 -0
  148. pyvbaanalysis-1.0.0/tests/test_rules_typeof_is_always_false.py +106 -0
  149. pyvbaanalysis-1.0.0/tests/test_rules_undeclared.py +131 -0
  150. pyvbaanalysis-1.0.0/tests/test_runtime_tables.py +39 -0
  151. pyvbaanalysis-1.0.0/tests/test_stripped_lines.py +50 -0
  152. pyvbaanalysis-1.0.0/tests/test_symbols.py +202 -0
  153. pyvbaanalysis-1.0.0/tests/test_token_helpers.py +86 -0
  154. pyvbaanalysis-1.0.0/tests/test_type_completion.py +92 -0
  155. pyvbaanalysis-1.0.0/tests/test_type_names.py +42 -0
  156. pyvbaanalysis-1.0.0/tools/extract_event_definitions.mjs +25 -0
  157. pyvbaanalysis-1.0.0/tools/extract_host_model.mjs +12 -0
  158. pyvbaanalysis-1.0.0/tools/extract_runtime_tables.mjs +10 -0
  159. pyvbaanalysis-1.0.0/tools/generate_diagnostics_catalogue.py +96 -0
@@ -0,0 +1,3 @@
1
+ # Normalize line endings to LF in the repository and working tree across
2
+ # platforms. Python and the vendored JSON data package stay LF everywhere.
3
+ * text=auto eol=lf
@@ -0,0 +1,13 @@
1
+ version: 2
2
+ updates:
3
+ # Keep the GitHub Actions pinned in the workflows current.
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+
9
+ # Keep the Python tooling and the runtime dependency current.
10
+ - package-ecosystem: pip
11
+ directory: /
12
+ schedule:
13
+ interval: weekly
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ # Run the lint, type, and test gate on every push to main and every pull request,
4
+ # so a regression is caught before it can reach a release.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ # A new push to the same branch or PR cancels the in-progress run.
15
+ concurrency:
16
+ group: ci-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ lint:
21
+ name: Lint and type check
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+ cache: pip
29
+ cache-dependency-path: pyproject.toml
30
+ - run: python -m pip install -e ".[dev]"
31
+ - name: Ruff
32
+ run: python -m ruff check .
33
+ - name: Mypy
34
+ run: python -m mypy pyvbaanalysis
35
+
36
+ test:
37
+ name: Test (Python ${{ matrix.python-version }})
38
+ runs-on: ubuntu-latest
39
+ strategy:
40
+ fail-fast: false
41
+ matrix:
42
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - uses: actions/setup-python@v5
46
+ with:
47
+ python-version: ${{ matrix.python-version }}
48
+ cache: pip
49
+ cache-dependency-path: pyproject.toml
50
+ - run: python -m pip install -e ".[dev]"
51
+ - name: Pytest
52
+ run: python -m pytest -q
@@ -0,0 +1,53 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes pyvbaanalysis to PyPI when a GitHub Release is published, using PyPI
4
+ # Trusted Publishing (OIDC), so no API tokens are stored anywhere.
5
+ #
6
+ # One-time setup before the first release (see CONTRIBUTING.md for the full steps):
7
+ # 1. PyPI -> Account -> Publishing -> add a pending publisher:
8
+ # Project name: pyvbaanalysis
9
+ # Owner: WilliamSmithEdward
10
+ # Repository name: pyVBAanalysis
11
+ # Workflow name: publish.yml
12
+ # Environment name: pypi
13
+ # 2. GitHub -> repo Settings -> Environments -> create an environment named `pypi`.
14
+
15
+ on:
16
+ release:
17
+ types: [published]
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ publish:
24
+ name: Build and publish to PyPI
25
+ runs-on: ubuntu-latest
26
+ environment:
27
+ name: pypi
28
+ url: https://pypi.org/p/pyvbaanalysis
29
+ permissions:
30
+ id-token: write # OIDC token for PyPI Trusted Publishing
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ - uses: actions/setup-python@v5
34
+ with:
35
+ python-version: "3.12"
36
+ cache: pip
37
+ cache-dependency-path: pyproject.toml
38
+ - name: Install package + dev/build tools
39
+ run: python -m pip install -e ".[dev,build]"
40
+ - name: Test gate (pytest / ruff / mypy)
41
+ run: |
42
+ python -m pytest -q
43
+ python -m ruff check .
44
+ python -m mypy pyvbaanalysis
45
+ - name: Build sdist + wheel
46
+ run: python -m build
47
+ - name: Upload build artifacts
48
+ uses: actions/upload-artifact@v4
49
+ with:
50
+ name: dist
51
+ path: dist/*
52
+ - name: Publish to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,36 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ *.egg
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+ .python-version
18
+
19
+ # Test / type / lint caches
20
+ .pytest_cache/
21
+ .mypy_cache/
22
+ .ruff_cache/
23
+ .coverage
24
+ .coverage.*
25
+ htmlcov/
26
+ .tox/
27
+
28
+ # Editors / OS
29
+ .vscode/
30
+ .idea/
31
+ *.swp
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Note: the vendored XLIDE data package under pyvbaanalysis/data/ is intentionally
36
+ # committed (it is the version-pinned contract surface), so it is NOT ignored here.
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ All notable changes to pyVBAanalysis are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
5
+ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html): a minor version
6
+ per milestone.
7
+
8
+ ## 1.0.0 - 2026-06-20
9
+
10
+ The first public release: a pure-Python static analyzer for Excel VBA with a
11
+ no-false-positive discipline, where a diagnostic is reported only when it is
12
+ provably correct and anything unknown or ambiguous stays quiet.
13
+
14
+ ### Analysis
15
+
16
+ * The complete analysis stack: lexer, parser, symbol and project index,
17
+ conditional compilation, type inference, the Excel host object model, the
18
+ member-completion surface, and the project-type registry.
19
+ * 85 diagnostic rules emitting a catalogue of 117 diagnostic codes, validated
20
+ against a corpus of 397 real Excel/VBE behavior cases.
21
+
22
+ ### Ingestion and entry points
23
+
24
+ * `analyze_module` for one module's source text, and `analyze_project` for a set
25
+ of modules analyzed together with cross-module context.
26
+ * `analyze_loose_file` / `analyze_loose_files` for loose `.bas` / `.cls` / `.frm`
27
+ export files, and `analyze_workbook` for VBA read directly out of Excel
28
+ workbooks. `build_project_index` and `analyze_module_options_for` expose the
29
+ per-module options for a custom pass.
30
+ * A `pyvbaanalysis.reader` package that strips the VBE export header, infers the
31
+ module kind, and reads modules from Excel files.
32
+ * A command-line interface: `python -m pyvbaanalysis PATH ...` over loose files,
33
+ folders, and Excel workbooks, with `--only`, `--severity`, `--select` /
34
+ `--ignore`, `--fail-level`, `--format`, and CI-friendly exit codes.
35
+ * The headline entry points are re-exported from the package root, and the package
36
+ ships a `py.typed` marker.
37
+
38
+ ### Packaging
39
+
40
+ * MIT license and a Trusted Publishing release workflow (OIDC, no API tokens).
41
+ * One runtime dependency, pyOpenVBA, used to read VBA out of Excel workbooks and
42
+ imported lazily.
43
+
44
+ ### Documentation
45
+
46
+ * A usage guide, an API reference, a generated diagnostics catalogue, and a
47
+ contributing guide.
@@ -0,0 +1,105 @@
1
+ # Contributing
2
+
3
+ pyVBAanalysis is a faithful Python port of the
4
+ [XLIDE](https://github.com/WilliamSmithEdward/xlide_vscode) VBA analyzer. The
5
+ overriding rule is the no-false-positive discipline: a diagnostic ships only when
6
+ it is provably correct. When in doubt, stay quiet.
7
+
8
+ ## Development setup
9
+
10
+ Python 3.10 or later.
11
+
12
+ ```
13
+ pip install -e ".[dev]"
14
+ ```
15
+
16
+ The `dev` extra adds the test, lint, and type tools. pyOpenVBA (the one runtime
17
+ dependency, used to read VBA out of Excel workbooks) is installed with the base
18
+ package, so the workbook reader is exercised by the test suite.
19
+
20
+ ## Local checks
21
+
22
+ Run all three before sending a change. They are the same gate CI enforces:
23
+
24
+ ```
25
+ pytest
26
+ ruff check .
27
+ mypy pyvbaanalysis
28
+ ```
29
+
30
+ `mypy` runs in `--strict` mode. The suite is fast (a few seconds); run it often.
31
+
32
+ ## The XLIDE port model
33
+
34
+ This repository ports XLIDE's TypeScript analyzer. XLIDE owns the language and
35
+ host knowledge; pyVBAanalysis reproduces its behavior in Python.
36
+
37
+ * The XLIDE source is expected as a sibling checkout at `../xlide_vscode`. The
38
+ data-extraction tools reference it through that relative path.
39
+ * XLIDE also owns the oracle: the Excel/VBE evidence corpus and the provenance
40
+ audit. pyVBAanalysis consumes the emitted evidence verbatim as both porting spec
41
+ and test fixtures. Do not reimplement the oracle here.
42
+ * The vendored data lives in `pyvbaanalysis/data/` (the host object model, the
43
+ runtime tables, the oracle cases, the rule metadata, and a manifest with
44
+ checksums). It is regenerated by the `tools/extract_*.mjs` scripts (run with
45
+ `npx tsx`) against the sibling XLIDE checkout, never hand-edited.
46
+
47
+ The build plan, parity inventory, and module-by-module port map are in
48
+ [agent.md](agent.md).
49
+
50
+ ## Adding or changing a rule
51
+
52
+ * Port the rule from its XLIDE source; match the behavior, not just the shape.
53
+ * A rule ships only with oracle backing. Validate it against the vendored corpus
54
+ and add direct tests for the positive and the no-false-positive control cases.
55
+ * The diagnostic rule registry order is a contract: it is the diagnostic
56
+ output order, and it must remain a faithful subsequence of XLIDE's `registry.ts`.
57
+ Place a new entry at its XLIDE position; do not reorder existing entries.
58
+ * Diagnostic codes and their metadata (default severity, category, evidence basis,
59
+ spec reference) live in the rule metadata. Regenerate the catalogue after a
60
+ metadata change:
61
+
62
+ ```
63
+ python tools/generate_diagnostics_catalogue.py
64
+ ```
65
+
66
+ ## Style
67
+
68
+ * Write plain ASCII. No em dashes or other non-ASCII in code, comments,
69
+ docstrings, or commit messages; use a comma, colon, period, or parentheses.
70
+ Test fixtures that deliberately exercise Unicode are the only exception.
71
+ * Avoid AI tells and marketing language. Prefer clear, direct prose.
72
+ * Keep changes small and focused, with names and structure that match the
73
+ surrounding code.
74
+ * Update the docs when behavior, setup, the API, or the architecture changes.
75
+ * Report status honestly: "done" means the checks above were run and passed.
76
+
77
+ ## Continuous integration
78
+
79
+ `.github/workflows/ci.yml` runs the lint, type, and test gate on every push to
80
+ `main` and every pull request, across Python 3.10 to 3.13. Dependabot
81
+ (`.github/dependabot.yml`) opens weekly pull requests to update the GitHub Actions
82
+ and the Python tooling.
83
+
84
+ ## Releasing
85
+
86
+ Publishing is automated by `.github/workflows/publish.yml`, which builds the sdist
87
+ and wheel and uploads them to PyPI through Trusted Publishing (OIDC, no API
88
+ tokens) when a GitHub Release is published.
89
+
90
+ One-time setup before the first release:
91
+
92
+ 1. On PyPI, go to Account -> Publishing and add a pending publisher with project
93
+ name `pyvbaanalysis`, owner `WilliamSmithEdward`, repository `pyVBAanalysis`,
94
+ workflow `publish.yml`, and environment `pypi`.
95
+ 2. On GitHub, go to repo Settings -> Environments and create an environment named
96
+ `pypi`. Optionally add required reviewers so a publish needs human approval.
97
+
98
+ To cut a release:
99
+
100
+ 1. Bump `__version__` in `pyvbaanalysis/__init__.py` (the single source of the
101
+ version) and update `CHANGELOG.md`. Commit.
102
+ 2. Tag and push: `git tag vX.Y.Z` then `git push origin vX.Y.Z`.
103
+ 3. Publish a GitHub Release for that tag (for example
104
+ `gh release create vX.Y.Z --notes-file <notes>`). Publishing the release runs
105
+ `publish.yml`, which re-runs the gate, builds, and uploads to PyPI.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 William Smith Edward
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyvbaanalysis
3
+ Version: 1.0.0
4
+ Summary: Pure-Python static analysis for Excel VBA
5
+ Project-URL: Homepage, https://github.com/WilliamSmithEdward/pyVBAanalysis
6
+ Project-URL: Repository, https://github.com/WilliamSmithEdward/pyVBAanalysis
7
+ Project-URL: Issues, https://github.com/WilliamSmithEdward/pyVBAanalysis/issues
8
+ Author: William Smith Edward
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: excel,linter,static-analysis,vba
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Quality Assurance
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: pyopenvba>=3.0.1
21
+ Provides-Extra: build
22
+ Requires-Dist: build>=1.2; extra == 'build'
23
+ Requires-Dist: twine>=5; extra == 'build'
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.11; extra == 'dev'
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Requires-Dist: ruff>=0.6; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # pyVBAanalysis
31
+
32
+ Static analysis for Excel VBA. It reads your macros and reports likely bugs and the
33
+ errors the VBA compiler would catch, without opening Excel or running any code.
34
+
35
+ Point it at a workbook or a set of exported module files, and it returns the
36
+ problems it finds, each with the exact line and a plain explanation.
37
+
38
+ ## What it checks
39
+
40
+ It looks for more than a hundred kinds of problem, including:
41
+
42
+ - Type errors, such as assigning a string to a `Long` or passing the wrong type to
43
+ a procedure.
44
+ - Undeclared variables and calls to procedures or members that do not exist.
45
+ - Code the VBA compiler rejects: duplicate declarations, a missing `Option
46
+ Explicit`, malformed statements, or a `Declare` that lacks `PtrSafe` on 64-bit
47
+ Office.
48
+ - Likely run-time failures, such as dividing by a constant zero or a type mismatch
49
+ from a bad conversion.
50
+
51
+ It only reports a problem when it can prove one, and stays quiet otherwise, so the
52
+ output does not bury you in false alarms.
53
+
54
+ ## Install
55
+
56
+ ```
57
+ pip install pyvbaanalysis
58
+ ```
59
+
60
+ Python 3.10 or later. Nothing else to set up.
61
+
62
+ ## Use it from Python
63
+
64
+ Analyze a workbook:
65
+
66
+ ```python
67
+ from pyvbaanalysis import analyze_workbook
68
+
69
+ for module, problems in analyze_workbook("Budget.xlsm").items():
70
+ for p in problems:
71
+ print(module, p.severity.value, p.code, p.message)
72
+ ```
73
+
74
+ Analyze a single module's source:
75
+
76
+ ```python
77
+ from pyvbaanalysis import analyze_module
78
+
79
+ source = "Sub Test()\n Dim n As Long\n n = \"oops\"\nEnd Sub\n"
80
+ for p in analyze_module(source):
81
+ print(p.code, p.message)
82
+ ```
83
+
84
+ Each result has a `code`, a `message`, a `severity` (`error`, `warning`, or
85
+ `information`), and a `span` giving the character offsets in the source.
86
+
87
+ Analyze several exported files together, so references between them resolve:
88
+
89
+ ```python
90
+ from pyvbaanalysis import analyze_loose_files
91
+
92
+ analyze_loose_files(["Module1.bas", "Sheet1.cls", "UserForm1.frm"])
93
+ ```
94
+
95
+ ## Use it from the command line
96
+
97
+ ```
98
+ pyvbaanalysis Budget.xlsm
99
+ pyvbaanalysis ./exported_modules --format json
100
+ pyvbaanalysis Budget.xlsm --only Sheet1
101
+ ```
102
+
103
+ A path can be a workbook, a folder of exported `.bas` / `.cls` / `.frm` files, or a
104
+ single file. The command exits 1 when it finds problems and 0 when the code is
105
+ clean, so it drops into a CI check.
106
+
107
+ ## Scope
108
+
109
+ This analyzes Excel VBA. It does not run macros and does not need Excel installed.
110
+ Word and PowerPoint are not supported.
111
+
112
+ ## Documentation
113
+
114
+ - [Usage guide](docs/usage.md)
115
+ - [API reference](docs/api-reference.md)
116
+ - [Diagnostic catalogue](docs/diagnostics-catalogue.md)
117
+
118
+ ## License
119
+
120
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,91 @@
1
+ # pyVBAanalysis
2
+
3
+ Static analysis for Excel VBA. It reads your macros and reports likely bugs and the
4
+ errors the VBA compiler would catch, without opening Excel or running any code.
5
+
6
+ Point it at a workbook or a set of exported module files, and it returns the
7
+ problems it finds, each with the exact line and a plain explanation.
8
+
9
+ ## What it checks
10
+
11
+ It looks for more than a hundred kinds of problem, including:
12
+
13
+ - Type errors, such as assigning a string to a `Long` or passing the wrong type to
14
+ a procedure.
15
+ - Undeclared variables and calls to procedures or members that do not exist.
16
+ - Code the VBA compiler rejects: duplicate declarations, a missing `Option
17
+ Explicit`, malformed statements, or a `Declare` that lacks `PtrSafe` on 64-bit
18
+ Office.
19
+ - Likely run-time failures, such as dividing by a constant zero or a type mismatch
20
+ from a bad conversion.
21
+
22
+ It only reports a problem when it can prove one, and stays quiet otherwise, so the
23
+ output does not bury you in false alarms.
24
+
25
+ ## Install
26
+
27
+ ```
28
+ pip install pyvbaanalysis
29
+ ```
30
+
31
+ Python 3.10 or later. Nothing else to set up.
32
+
33
+ ## Use it from Python
34
+
35
+ Analyze a workbook:
36
+
37
+ ```python
38
+ from pyvbaanalysis import analyze_workbook
39
+
40
+ for module, problems in analyze_workbook("Budget.xlsm").items():
41
+ for p in problems:
42
+ print(module, p.severity.value, p.code, p.message)
43
+ ```
44
+
45
+ Analyze a single module's source:
46
+
47
+ ```python
48
+ from pyvbaanalysis import analyze_module
49
+
50
+ source = "Sub Test()\n Dim n As Long\n n = \"oops\"\nEnd Sub\n"
51
+ for p in analyze_module(source):
52
+ print(p.code, p.message)
53
+ ```
54
+
55
+ Each result has a `code`, a `message`, a `severity` (`error`, `warning`, or
56
+ `information`), and a `span` giving the character offsets in the source.
57
+
58
+ Analyze several exported files together, so references between them resolve:
59
+
60
+ ```python
61
+ from pyvbaanalysis import analyze_loose_files
62
+
63
+ analyze_loose_files(["Module1.bas", "Sheet1.cls", "UserForm1.frm"])
64
+ ```
65
+
66
+ ## Use it from the command line
67
+
68
+ ```
69
+ pyvbaanalysis Budget.xlsm
70
+ pyvbaanalysis ./exported_modules --format json
71
+ pyvbaanalysis Budget.xlsm --only Sheet1
72
+ ```
73
+
74
+ A path can be a workbook, a folder of exported `.bas` / `.cls` / `.frm` files, or a
75
+ single file. The command exits 1 when it finds problems and 0 when the code is
76
+ clean, so it drops into a CI check.
77
+
78
+ ## Scope
79
+
80
+ This analyzes Excel VBA. It does not run macros and does not need Excel installed.
81
+ Word and PowerPoint are not supported.
82
+
83
+ ## Documentation
84
+
85
+ - [Usage guide](docs/usage.md)
86
+ - [API reference](docs/api-reference.md)
87
+ - [Diagnostic catalogue](docs/diagnostics-catalogue.md)
88
+
89
+ ## License
90
+
91
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,86 @@
1
+ # API reference
2
+
3
+ The package root re-exports the headline entry points; the rest of the public
4
+ surface lives in the submodules listed below. This page is an import map, not a
5
+ full autodoc dump. For task-oriented examples see [usage.md](usage.md).
6
+
7
+ ## Headline API (`pyvbaanalysis`)
8
+
9
+ These are re-exported at the package root for convenience:
10
+
11
+ | Symbol | Role |
12
+ | --- | --- |
13
+ | `analyze_module(source, opts=None)` | Analyze one module's source; returns `list[VbaDiagnostic]`. Never raises. |
14
+ | `analyze_project(modules, *, only=None, severity_overrides=None, conditional_compilation=None)` | Analyze a set of `ModuleInput`s with cross-module context; returns `dict[str, list[VbaDiagnostic]]`. |
15
+ | `analyze_loose_file(path, *, severity_overrides=None, conditional_compilation=None)` | Analyze one `.bas`/`.cls`/`.frm` file. |
16
+ | `analyze_loose_files(paths, *, only=None, severity_overrides=None, conditional_compilation=None)` | Analyze several loose files as one project. |
17
+ | `analyze_workbook(path, *, only=None, severity_overrides=None, conditional_compilation=None)` | Analyze the VBA in an Excel workbook (via pyOpenVBA). |
18
+ | `analyze_module_options_for(index, name, kind, *, severity_overrides=None, conditional_compilation=None)` | Build per-module `AnalyzeModuleOptions` from a populated `ProjectIndex`. |
19
+ | `build_project_index(modules)` | A `ProjectIndex` with every module registered. |
20
+ | `AnalyzeModuleOptions` | Inputs for `analyze_module` (name, kind, project context, overrides). |
21
+ | `VbaDiagnostic` | A single diagnostic (`code`, `message`, `severity`, `span`, `spec_reference`). |
22
+ | `ModuleInput` | One module fed into a project (`module_name`, `module_kind`, `source`). |
23
+ | `ModuleSymbolKind` | `STANDARD`, `CLASS`, `DOCUMENT`, `USERFORM`. |
24
+ | `ProjectIndex` | The cross-module symbol/type index. |
25
+ | `__version__` | The package version string. |
26
+
27
+ ## Engine and result model (`pyvbaanalysis.diagnostics`)
28
+
29
+ * Entry point: `analyze_module`, `AnalyzeModuleOptions`, `RulePassContext`, `PushFn`,
30
+ `is_object_module_kind`.
31
+ * Result model: `VbaDiagnostic`, `VbaDiagnosticData`, `VbaEdit`, and the enums
32
+ `DiagnosticSeverity`, `DiagnosticCategory`, `DiagnosticEvidenceKind`,
33
+ `DiagnosticSuppressionScope`.
34
+ * Registry and metadata: `DIAGNOSTIC_RULE_REGISTRY`, `DiagnosticRuleEntry`,
35
+ `STRUCTURAL_DIAGNOSTIC_RULES`, `DIAGNOSTIC_RULES`, `DiagnosticRuleMetadata`,
36
+ `diagnostic_metadata_for_code`, `rule_metadata_by_code`, `load_rule_metadata`,
37
+ `normalize_diagnostic_severity_override`.
38
+
39
+ ## Readers (`pyvbaanalysis.reader`)
40
+
41
+ * High level: `analyze_loose_file`, `analyze_loose_files`, `analyze_workbook`,
42
+ `load_loose_module`, `read_workbook_modules`.
43
+ * Building blocks: `LoadedModule`, `strip_export_header`, `classify_module_kind`,
44
+ `module_name_from_text`, `loaded_module_from_text`, `WorkbookReadError`,
45
+ `LOOSE_EXTENSIONS`, `EXCEL_EXTENSIONS`.
46
+
47
+ ## Project model (`pyvbaanalysis.symbols`)
48
+
49
+ * Index: `ProjectIndex`, `ProjectIndexOptions`, `ModuleInput`, `ReferenceScope`,
50
+ `ShadowedSpan`.
51
+ * Module symbols: `build_module_symbols`, `BuildModuleSymbolsOptions`,
52
+ `ModuleSymbols`, `ModuleSymbolKind`, `SymbolVisibility`, `VbaSymbol`,
53
+ `VbaSymbolKind`, `VbaSymbolAttribute`, `VbaProcedureSignature`,
54
+ `VbaProcedureParam`, and the predicates `is_procedure_kind`,
55
+ `is_bare_callable_kind`, `procedure_signature_from_symbol`,
56
+ `qualified_procedure_key`.
57
+ * Name resolution: `resolve_bare_identifier_binding`, `source_identifier_names`,
58
+ `BareIdentifierContext`, `BareIdentifierResolution`.
59
+
60
+ ## Lower-level building blocks
61
+
62
+ These are the analysis layers the engine is built on. Most consumers do not need
63
+ them directly, but they are public and stable.
64
+
65
+ | Package | What it provides |
66
+ | --- | --- |
67
+ | `pyvbaanalysis.lexer` | `tokenize`, `tokenize_cached`, `VbaToken`, `TokenKind`, and token helpers. |
68
+ | `pyvbaanalysis.parser` | `parse_module`, `parse_expression`, `ModuleNode`, and the AST nodes. |
69
+ | `pyvbaanalysis.conditional` | Conditional-compilation indexing and activity (`index_conditional_compilation`, `evaluate_conditional_expression`, ...). |
70
+ | `pyvbaanalysis.types` | Type-name helpers: `normalize_type`, `is_known_scalar_type`, `is_numeric_type`, `numeric_literal_bounds`. |
71
+ | `pyvbaanalysis.completion` | Member-completion surface and project-type resolution (`resolve_member_surface_at`, `resolve_type_name`, ...). |
72
+ | `pyvbaanalysis.host` | The Excel host object model (`get_excel_object_model`, `resolve_host_alias`, ...). |
73
+ | `pyvbaanalysis.runtime` | VBA runtime functions, constants, and objects (`resolve_runtime_function`, ...). |
74
+ | `pyvbaanalysis.call` | Call-statement shape helpers. |
75
+ | `pyvbaanalysis.flow` | Procedure labels and unstructured-flow detection. |
76
+ | `pyvbaanalysis.constants` | Integer constant-expression evaluation. |
77
+
78
+ Many helper free functions inside these packages are intentionally internal even
79
+ when importable. The symbols listed above are the supported surface.
80
+
81
+ ## A note on the package root
82
+
83
+ The package root exports only the headline API plus `__version__`. Everything
84
+ else is imported from its submodule, e.g.
85
+ `from pyvbaanalysis.diagnostics import DiagnosticSeverity` or
86
+ `from pyvbaanalysis.symbols import ModuleSymbols`.