sourcecode 1.41.0__tar.gz → 1.44.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.
- {sourcecode-1.41.0 → sourcecode-1.44.0}/.gitignore +4 -1
- {sourcecode-1.41.0 → sourcecode-1.44.0}/CHANGELOG.md +72 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/PKG-INFO +3 -3
- {sourcecode-1.41.0 → sourcecode-1.44.0}/README.md +2 -2
- {sourcecode-1.41.0 → sourcecode-1.44.0}/pyproject.toml +1 -1
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/repository_ir.py +101 -3
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_impact.py +66 -0
- sourcecode-1.41.0/supabase/.temp/cli-latest +0 -1
- {sourcecode-1.41.0 → sourcecode-1.44.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/.ruff.toml +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/LICENSE +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/SECURITY.md +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/raw +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/cli.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/license.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/functions/README.md +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-1.41.0 → sourcecode-1.44.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -1,5 +1,77 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.44.0] — 2026-06-16
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **CH-004c — annotation-free structural classes dropped their inheritance edges.**
|
|
7
|
+
`build_repo_ir`'s fast pre-scan skips files with no recognized annotation marker,
|
|
8
|
+
emitting only minimal class-name symbols (for same-package resolution) and **no
|
|
9
|
+
relations**. A class with no annotation and no injected field of its own that
|
|
10
|
+
participates purely structurally — `class X extends Base implements I {}` — therefore
|
|
11
|
+
lost its `extends`/`implements` edges, so `implementation_graph` could not link
|
|
12
|
+
sub→supertype and impact analysis could not traverse the hierarchy. The pre-scan now
|
|
13
|
+
also keeps a file when a type declaration carries an `extends`/`implements` clause
|
|
14
|
+
(`_INHERIT_PRESCAN_RE`), routing it through full extraction so its inheritance edges
|
|
15
|
+
are built and resolved in pass 2 with the same-package map. Annotation-free leaf
|
|
16
|
+
classes with no inheritance still skip, preserving the pre-scan optimization. Closes
|
|
17
|
+
the last open layer of CH-004 (a/b shipped in 1.43.0).
|
|
18
|
+
|
|
19
|
+
## [1.43.0] — 2026-06-16
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **CH-004a — impact graph dropped field-injection-only classes.** `build_repo_ir`'s fast
|
|
23
|
+
pre-scan skips files with no recognized annotation marker; the marker set included
|
|
24
|
+
`@Inject` but not `@Autowired`, `@Resource`, `@Qualifier`, or `@Value`. A class wired
|
|
25
|
+
purely by field injection with no class-level stereotype (e.g. an abstract base controller
|
|
26
|
+
that holds the services its concrete subclasses inherit) was skipped entirely, so its
|
|
27
|
+
`injects` edges never existed and `impact-chain` could not traverse through it. Added the
|
|
28
|
+
field/setter-injection annotations (`@Autowired`, `@Resource`, `@Qualifier`, `@Value`,
|
|
29
|
+
`@PersistenceContext`, `@PersistenceUnit`) to the pre-scan marker set.
|
|
30
|
+
- **CH-004b — same-package supertypes were not FQN-resolved.** The `extends`/`implements`
|
|
31
|
+
edge builder resolved supertype names only via `import_map`, so a same-package
|
|
32
|
+
`extends Base` (which needs no Java import) produced a **bare-name** edge target. The
|
|
33
|
+
`implementation_graph` could then not link sub→supertype, making same-package class
|
|
34
|
+
hierarchies invisible to impact analysis. Supertypes are now resolved via
|
|
35
|
+
`_resolve_dep_type` (import + same-package + wildcard), matching `injects`/constructor edges.
|
|
36
|
+
|
|
37
|
+
Both fixes were surfaced by a field test on BroadleafCommerce (2985-file monolith); they
|
|
38
|
+
under-reported blast radius on any large repo, not just that one. See
|
|
39
|
+
`docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md`.
|
|
40
|
+
|
|
41
|
+
## [1.42.0] — 2026-06-16
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- **Fase 22 — type-usage edges in `impact-chain` (CH-003).** Value/DTO/response
|
|
45
|
+
types previously had an invisible blast radius: the impact graph modelled call and
|
|
46
|
+
DI/injection edges but not how a type is wired *by type*. Two new edges close this:
|
|
47
|
+
- **`returns`** — `method → returnTypeFQN` (method-level). For a `@ResponseBody`
|
|
48
|
+
handler returning a domain type this is the only link from the type back to its
|
|
49
|
+
endpoint, so `_collect_endpoints` now surfaces that route precisely.
|
|
50
|
+
- **`instantiates`** — `class → T` for `new T(...)`, giving build-only value types
|
|
51
|
+
(commands, receipts) a visible blast radius. Controller-like classes are excluded
|
|
52
|
+
(already covered precisely by `returns`) to avoid broadening a DTO's impact to
|
|
53
|
+
every route on the controller.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- **Inline-annotation method parsing.** `_METHOD_DECL_RE` could not match a
|
|
57
|
+
modifier-position annotation (`public @ResponseBody Vets foo()`); the whole
|
|
58
|
+
declaration failed and the method — its endpoint *and* return type — was silently
|
|
59
|
+
dropped. Inline annotations are now consumed and folded into the method's annotation
|
|
60
|
+
set. Recovers, e.g., the `GET /vets` handler in spring-petclinic `VetController`.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
- **Safety guard for type-usage blind spots (CH-003 Part 1).** A fully empty blast
|
|
64
|
+
radius on a positively-identified plain value type (node present, `symbol_kind` in
|
|
65
|
+
class/enum/record, no stereotype annotation, role `other`, not a controller) is no
|
|
66
|
+
longer reported at `confidence: high` — it drops to `low` with a warning that an
|
|
67
|
+
empty result is not proof the type is unused. Spine symbols and incomplete-IR cases
|
|
68
|
+
keep prior behaviour. The guard now stays dormant when real type-usage edges exist.
|
|
69
|
+
|
|
70
|
+
### Field test (spring-petclinic #2333)
|
|
71
|
+
- `impact-chain Vets`: was `confidence: high` with 0 callers / 0 endpoints (a dangerous
|
|
72
|
+
false zero) → now `high`, caller `showResourcesVetList`, endpoint `GET /vets`.
|
|
73
|
+
- `impact-chain VetController`: was 1 of 2 endpoints → both (`GET /vets` + `/vets.html`).
|
|
74
|
+
|
|
3
75
|
## [1.41.0] — 2026-06-16
|
|
4
76
|
|
|
5
77
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.44.0
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: agents,ai,codebase,context,developer-tools,llm
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
42
42
|
|
|
43
|
-

|
|
44
44
|

|
|
45
45
|
|
|
46
46
|
---
|
|
@@ -114,7 +114,7 @@ pipx install sourcecode
|
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
sourcecode version
|
|
117
|
-
# sourcecode 1.
|
|
117
|
+
# sourcecode 1.44.0
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -76,7 +76,7 @@ pipx install sourcecode
|
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
sourcecode version
|
|
79
|
-
# sourcecode 1.
|
|
79
|
+
# sourcecode 1.44.0
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
---
|
|
@@ -131,9 +131,22 @@ _CLASS_DECL_RE = re.compile(
|
|
|
131
131
|
r'\s*\{',
|
|
132
132
|
)
|
|
133
133
|
|
|
134
|
+
# CH-004c: detect a type declaration that participates structurally via inheritance
|
|
135
|
+
# (`class X extends Base` / `interface I extends A` / `class C implements I`). Used by
|
|
136
|
+
# the fast pre-scan to NOT skip annotation-free files that still own extends/implements
|
|
137
|
+
# edges the impact graph needs. `[^{;]*?` keeps the match within a single declaration
|
|
138
|
+
# head (stops at the body `{` or a `;`), matching the precision of _CLASS_DECL_RE.
|
|
139
|
+
_INHERIT_PRESCAN_RE = re.compile(
|
|
140
|
+
r'\b(?:class|interface)\s+[A-Z]\w*[^{;]*?\b(?:extends|implements)\b'
|
|
141
|
+
)
|
|
142
|
+
|
|
134
143
|
_METHOD_DECL_RE = re.compile(
|
|
135
144
|
r'^(?P<modifiers>(?:(?:public|private|protected|static|final|synchronized'
|
|
136
145
|
r'|abstract|default|native|strictfp|override)\s+)*)'
|
|
146
|
+
# Inline (modifier-position) annotations, e.g. `public @ResponseBody Vets foo()`
|
|
147
|
+
# or `@Valid`, `@Nonnull`. Without this the whole declaration fails to match and
|
|
148
|
+
# the method (its endpoint + return-type edge) is silently dropped (CH-003/Fase 22).
|
|
149
|
+
r'(?P<inline_anns>(?:@[\w.]+(?:\s*\([^)]*\))?\s+)*)'
|
|
137
150
|
r'(?:<[\w,\s?]+>\s+)?'
|
|
138
151
|
r'(?P<return_type>(?:void|boolean|byte|char|short|int|long|float|double|String|[\w.<>\[\]?,]+)\s+)'
|
|
139
152
|
r'(?P<name>[a-z_]\w*)\s*\(',
|
|
@@ -830,6 +843,11 @@ def _extract_symbols(
|
|
|
830
843
|
if mname not in _JAVA_KEYWORDS:
|
|
831
844
|
fqn = f"{class_fqn}#{mname}"
|
|
832
845
|
modifiers = _parse_modifier_str(mth_m.group("modifiers") or "")
|
|
846
|
+
# Fold modifier-position inline annotations into pending_anns so
|
|
847
|
+
# symbol_kind / endpoint detection see them (e.g. @ResponseBody).
|
|
848
|
+
for _inl in re.findall(r'@[\w.]+', mth_m.group("inline_anns") or ""):
|
|
849
|
+
if _inl not in pending_anns:
|
|
850
|
+
pending_anns.append(_inl)
|
|
833
851
|
used = _resolve_types_from_text(stripped, import_map)
|
|
834
852
|
conf = "high" if ("public" in modifiers or pending_anns) else "medium"
|
|
835
853
|
|
|
@@ -1256,7 +1274,11 @@ def _build_relations(
|
|
|
1256
1274
|
# commas so each base produces its own edge and the reverse graph sees
|
|
1257
1275
|
# every supertype (not a single mangled token).
|
|
1258
1276
|
for base in _split_supertype_list(extends_str):
|
|
1259
|
-
|
|
1277
|
+
# CH-004: resolve same-package / wildcard-imported supertypes to their
|
|
1278
|
+
# FQN (not just import_map), else a same-package `extends Base` stays a
|
|
1279
|
+
# bare name and the implementation_graph cannot link sub→supertype.
|
|
1280
|
+
_sbase = re.sub(r'<.*', '', base).strip()
|
|
1281
|
+
to = _resolve_dep_type(_sbase) or import_map.get(_sbase, base)
|
|
1260
1282
|
edges.append(RelationEdge(
|
|
1261
1283
|
from_symbol=class_fqn,
|
|
1262
1284
|
to_symbol=to,
|
|
@@ -1267,7 +1289,8 @@ def _build_relations(
|
|
|
1267
1289
|
|
|
1268
1290
|
if implements_str:
|
|
1269
1291
|
for base in _split_supertype_list(implements_str):
|
|
1270
|
-
|
|
1292
|
+
_sbase = re.sub(r'<.*', '', base).strip()
|
|
1293
|
+
to = _resolve_dep_type(_sbase) or import_map.get(_sbase, base)
|
|
1271
1294
|
edges.append(RelationEdge(
|
|
1272
1295
|
from_symbol=class_fqn,
|
|
1273
1296
|
to_symbol=to,
|
|
@@ -1330,6 +1353,32 @@ def _build_relations(
|
|
|
1330
1353
|
evidence={"type": "annotation", "value": ann},
|
|
1331
1354
|
))
|
|
1332
1355
|
|
|
1356
|
+
# ── Type-usage: return-type edges (CH-003 / Fase 22) ──────────────────────
|
|
1357
|
+
# A method's declared return type is a real dependency edge the call/DI graph
|
|
1358
|
+
# misses: for a value/DTO/response type returned (esp. @ResponseBody) by a
|
|
1359
|
+
# controller handler, this is the ONLY link from the type back to its endpoint.
|
|
1360
|
+
# method → returnTypeFQN, type="returns". Resolution reuses _resolve_dep_type,
|
|
1361
|
+
# so only in-repo / imported types produce edges (primitives & void are skipped).
|
|
1362
|
+
_PRIMITIVE_RETURNS: frozenset[str] = frozenset({
|
|
1363
|
+
"void", "boolean", "byte", "char", "short", "int", "long", "float",
|
|
1364
|
+
"double", "String", "Object",
|
|
1365
|
+
})
|
|
1366
|
+
for sym in symbols:
|
|
1367
|
+
if sym.type != "method" or not sym.return_type:
|
|
1368
|
+
continue
|
|
1369
|
+
_ret_base = re.sub(r'<.*', '', sym.return_type).replace("[]", "").strip()
|
|
1370
|
+
if not _ret_base or _ret_base in _PRIMITIVE_RETURNS or not _ret_base[0].isupper():
|
|
1371
|
+
continue
|
|
1372
|
+
_ret_fqn = _resolve_dep_type(_ret_base)
|
|
1373
|
+
if _ret_fqn and _ret_fqn != _enclosing_class(sym.symbol):
|
|
1374
|
+
edges.append(RelationEdge(
|
|
1375
|
+
from_symbol=sym.symbol,
|
|
1376
|
+
to_symbol=_ret_fqn,
|
|
1377
|
+
type="returns",
|
|
1378
|
+
confidence="high",
|
|
1379
|
+
evidence={"type": "return_type", "value": sym.return_type},
|
|
1380
|
+
))
|
|
1381
|
+
|
|
1333
1382
|
_class_syms = [s for s in symbols if s.type in ("class", "interface") and "#" not in s.symbol]
|
|
1334
1383
|
|
|
1335
1384
|
# Strip comments before event scanning to prevent Javadoc examples from
|
|
@@ -1425,6 +1474,46 @@ def _build_relations(
|
|
|
1425
1474
|
evidence={"type": "signature", "value": f"{ev_label}<{event_simple}>"},
|
|
1426
1475
|
))
|
|
1427
1476
|
|
|
1477
|
+
# ── Type-usage: instantiation edges (CH-003 / Fase 22) ────────────────────
|
|
1478
|
+
# `new T(...)` couples the instantiating class to T — a type-usage edge the
|
|
1479
|
+
# call/DI graph misses. Attributed at class level (one primary type per file,
|
|
1480
|
+
# mirroring the publishes_event scan). Controllers are EXCLUDED: their value-type
|
|
1481
|
+
# coupling is already covered precisely by `returns` edges, and a class-level
|
|
1482
|
+
# edge from a controller would broaden a DTO's impact to every route on that
|
|
1483
|
+
# controller (false breadth). Method-level attribution is a future refinement.
|
|
1484
|
+
# Controller-like = class-level @RestController/@Controller OR a class that owns
|
|
1485
|
+
# any endpoint-handler method (mappings are often only method-level).
|
|
1486
|
+
_classes_with_endpoints = {
|
|
1487
|
+
_enclosing_class(s.symbol) for s in symbols if s.symbol_kind == "endpoint"
|
|
1488
|
+
}
|
|
1489
|
+
_instantiating_controllers = {
|
|
1490
|
+
s.symbol for s in symbols
|
|
1491
|
+
if s.type in ("class", "interface")
|
|
1492
|
+
and (
|
|
1493
|
+
any(a in ("@RestController", "@Controller") for a in s.annotations)
|
|
1494
|
+
or s.symbol in _classes_with_endpoints
|
|
1495
|
+
)
|
|
1496
|
+
}
|
|
1497
|
+
_non_controller_classes = [
|
|
1498
|
+
s for s in _class_syms if s.symbol not in _instantiating_controllers
|
|
1499
|
+
]
|
|
1500
|
+
if _non_controller_classes:
|
|
1501
|
+
_inst_targets: set[str] = set()
|
|
1502
|
+
for _m in re.finditer(r'\bnew\s+([A-Z]\w*)\s*[(<]', _source_no_comments):
|
|
1503
|
+
_t_fqn = _resolve_dep_type(_m.group(1))
|
|
1504
|
+
if _t_fqn:
|
|
1505
|
+
_inst_targets.add(_t_fqn)
|
|
1506
|
+
for cls_sym in _non_controller_classes:
|
|
1507
|
+
for _tgt in sorted(_inst_targets):
|
|
1508
|
+
if _tgt != cls_sym.symbol:
|
|
1509
|
+
edges.append(RelationEdge(
|
|
1510
|
+
from_symbol=cls_sym.symbol,
|
|
1511
|
+
to_symbol=_tgt,
|
|
1512
|
+
type="instantiates",
|
|
1513
|
+
confidence="medium",
|
|
1514
|
+
evidence={"type": "method_call", "value": f"new {_tgt.split('.')[-1]}(...)"},
|
|
1515
|
+
))
|
|
1516
|
+
|
|
1428
1517
|
seen: set[tuple[str, str, str]] = set()
|
|
1429
1518
|
unique: list[RelationEdge] = []
|
|
1430
1519
|
for e in edges:
|
|
@@ -3052,6 +3141,14 @@ def build_repo_ir(
|
|
|
3052
3141
|
'@RequiredArgsConstructor', '@AllArgsConstructor',
|
|
3053
3142
|
'@Inject', '@ApplicationScoped', '@RequestScoped', '@Singleton',
|
|
3054
3143
|
'@EnableMethodSecurity', '@EnableGlobalMethodSecurity',
|
|
3144
|
+
# Field/setter injection markers (CH-004). A class wired purely by field
|
|
3145
|
+
# injection — no class-level stereotype — is still a node in the DI graph:
|
|
3146
|
+
# e.g. an abstract base controller that holds @Autowired/@Resource services
|
|
3147
|
+
# its concrete subclasses inherit. Omitting these pre-scan-skips such classes,
|
|
3148
|
+
# dropping their injects edges, so impact-chain cannot reach callers through
|
|
3149
|
+
# them (Broadleaf: AbstractCheckoutController → checkout endpoints went missing).
|
|
3150
|
+
'@Autowired', '@Resource', '@Qualifier', '@Value',
|
|
3151
|
+
'@PersistenceContext', '@PersistenceUnit',
|
|
3055
3152
|
# JPA / persistence (needed for stereotype detection in all commands)
|
|
3056
3153
|
'@Entity', '@MappedSuperclass', '@Embeddable',
|
|
3057
3154
|
# AOP / messaging / event sourcing
|
|
@@ -3098,7 +3195,8 @@ def build_repo_ir(
|
|
|
3098
3195
|
_meta_chars_read += len(source)
|
|
3099
3196
|
# Fast pre-scan: if file has no relevant annotations skip full extraction.
|
|
3100
3197
|
# Still register package/class name for same-package resolution.
|
|
3101
|
-
if not any(marker in source for marker in _effective_markers)
|
|
3198
|
+
if not any(marker in source for marker in _effective_markers) \
|
|
3199
|
+
and not _INHERIT_PRESCAN_RE.search(source):
|
|
3102
3200
|
pkg_m = _PKG_RE.search(source)
|
|
3103
3201
|
_pkg = pkg_m.group(1) if pkg_m else ""
|
|
3104
3202
|
# Minimal class-name symbols for same-package map (no methods/fields)
|
|
@@ -129,6 +129,50 @@ class ImpactChainResult:
|
|
|
129
129
|
return d
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
# CH-003 — value/DTO type blind-spot detection
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
# The impact graph models call + DI/injection edges but not *type-usage* edges
|
|
136
|
+
# (constructor instantiation `new T()`, field/local-variable type, and method
|
|
137
|
+
# return type incl. @ResponseBody). For a service/repository the call+DI edges
|
|
138
|
+
# cover the real blast radius; for a value/DTO/response type they cover nothing,
|
|
139
|
+
# so its impact is invisible — and an all-zero result reported at confidence=high
|
|
140
|
+
# reads as "globally dead" (a dangerous false negative). Until type-usage edges
|
|
141
|
+
# are modelled (Fase 22 / CH-002), positively identify plain value types and
|
|
142
|
+
# downgrade confidence + warn instead of asserting an empty high-confidence result.
|
|
143
|
+
_STEREOTYPE_ANNOTATIONS = frozenset({
|
|
144
|
+
"@Service", "@Repository", "@Controller", "@RestController",
|
|
145
|
+
"@Component", "@Configuration", "@ControllerAdvice",
|
|
146
|
+
"@RestControllerAdvice", "@Bean",
|
|
147
|
+
})
|
|
148
|
+
_VALUE_TYPE_KINDS = frozenset({"class", "enum", "record"})
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _is_unmodeled_value_type(cir, class_fqn: str, model) -> bool:
|
|
152
|
+
"""True iff class_fqn is positively a plain value/DTO type whose blast radius
|
|
153
|
+
flows only through type-usage edges the impact graph does not model.
|
|
154
|
+
|
|
155
|
+
Conservative: returns False whenever the type cannot be positively confirmed
|
|
156
|
+
(node metadata absent, stereotype annotation present, recognized Spring role,
|
|
157
|
+
or controller) so spine symbols and incomplete-IR cases keep legacy behaviour.
|
|
158
|
+
"""
|
|
159
|
+
graph = (getattr(cir, "_raw_ir", None) or {}).get("graph") or {}
|
|
160
|
+
node = next((n for n in (graph.get("nodes") or []) if n.get("fqn") == class_fqn), None)
|
|
161
|
+
if node is None:
|
|
162
|
+
return False # cannot confirm — stay conservative (preserves IC-V3)
|
|
163
|
+
if (node.get("symbol_kind") or node.get("type")) not in _VALUE_TYPE_KINDS:
|
|
164
|
+
return False # interface / annotation / bean-method etc.
|
|
165
|
+
anns = node.get("annotations") or []
|
|
166
|
+
if any(a.split("(", 1)[0] in _STEREOTYPE_ANNOTATIONS for a in anns):
|
|
167
|
+
return False # Spring stereotype bean — spine participant
|
|
168
|
+
if (node.get("role") or "other") != "other":
|
|
169
|
+
return False # recognized Spring role (repository/service/controller/mapper)
|
|
170
|
+
controllers = getattr(getattr(model, "endpoint_index", None), "controller_fqns", frozenset())
|
|
171
|
+
if class_fqn in controllers:
|
|
172
|
+
return False
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
|
|
132
176
|
# ---------------------------------------------------------------------------
|
|
133
177
|
# Symbol resolution
|
|
134
178
|
# ---------------------------------------------------------------------------
|
|
@@ -706,9 +750,31 @@ class ImpactOrchestrator:
|
|
|
706
750
|
impact_findings_raw,
|
|
707
751
|
)
|
|
708
752
|
|
|
753
|
+
# CH-003: empty blast radius on a positively-identified value/DTO type is a
|
|
754
|
+
# type-usage blind spot, not proof of dead code — warn + drop confidence.
|
|
755
|
+
empty_blast = (
|
|
756
|
+
not direct_callers and not indirect_callers
|
|
757
|
+
and not endpoints_affected and not subtype_classes_added
|
|
758
|
+
)
|
|
759
|
+
value_type_blind_spot = (
|
|
760
|
+
empty_blast
|
|
761
|
+
and "#" not in resolved_symbol
|
|
762
|
+
and resolution != "not_found"
|
|
763
|
+
and _is_unmodeled_value_type(cir, resolved_symbol, model)
|
|
764
|
+
)
|
|
765
|
+
if value_type_blind_spot:
|
|
766
|
+
warnings.append(
|
|
767
|
+
"Type-usage edges not modeled (CH-003): this type's blast radius flows "
|
|
768
|
+
"through instantiation (new T()), field/local types, and method return "
|
|
769
|
+
"types (incl. @ResponseBody) — edges impact-chain does not yet track. "
|
|
770
|
+
"An empty result is NOT proof the type is unused."
|
|
771
|
+
)
|
|
772
|
+
|
|
709
773
|
confidence: str
|
|
710
774
|
if resolution == "not_found":
|
|
711
775
|
confidence = "low"
|
|
776
|
+
elif value_type_blind_spot:
|
|
777
|
+
confidence = "low"
|
|
712
778
|
elif resolution == "partial" or warnings:
|
|
713
779
|
confidence = "medium"
|
|
714
780
|
else:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v2.106.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|