sourcecode 1.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
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/__init__.py +1 -1
- sourcecode/architecture_analyzer.py +21 -1
- sourcecode/cli.py +7 -1
- sourcecode/confidence_analyzer.py +21 -0
- sourcecode/detectors/java.py +37 -2
- sourcecode/env_analyzer.py +6 -0
- sourcecode/metrics_analyzer.py +24 -0
- sourcecode/schema.py +1 -0
- sourcecode/semantic_analyzer.py +152 -0
- {sourcecode-1.1.0.dist-info → sourcecode-1.2.0.dist-info}/METADATA +1 -1
- {sourcecode-1.1.0.dist-info → sourcecode-1.2.0.dist-info}/RECORD +14 -14
- {sourcecode-1.1.0.dist-info → sourcecode-1.2.0.dist-info}/WHEEL +0 -0
- {sourcecode-1.1.0.dist-info → sourcecode-1.2.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.1.0.dist-info → sourcecode-1.2.0.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
|
@@ -183,7 +183,11 @@ class ArchitectureAnalyzer:
|
|
|
183
183
|
if ddd_result is not None:
|
|
184
184
|
ddd_pattern, ddd_layers, ddd_contexts, ddd_layer_names = ddd_result
|
|
185
185
|
domains_for_ddd = self._cluster_domains(filtered) if len(filtered) >= 2 else []
|
|
186
|
-
|
|
186
|
+
module_files = self._build_ddd_module_files(sm.file_paths, ddd_contexts)
|
|
187
|
+
bc_list = [
|
|
188
|
+
BoundedContext(name=n, modules=module_files.get(n, []), confidence="high")
|
|
189
|
+
for n in ddd_contexts
|
|
190
|
+
]
|
|
187
191
|
return ArchitectureAnalysis(
|
|
188
192
|
requested=True,
|
|
189
193
|
pattern=ddd_pattern,
|
|
@@ -414,6 +418,22 @@ class ArchitectureAnalyzer:
|
|
|
414
418
|
]
|
|
415
419
|
return "ddd", arch_layers, bounded_context_names, ddd_layer_names
|
|
416
420
|
|
|
421
|
+
def _build_ddd_module_files(
|
|
422
|
+
self, paths: list[str], bounded_context_names: list[str]
|
|
423
|
+
) -> "dict[str, list[str]]":
|
|
424
|
+
"""Build a mapping of DDD module name → list of file paths."""
|
|
425
|
+
_DDD_LAYERS = frozenset({"application", "domain", "infrastructure"})
|
|
426
|
+
module_files: dict[str, list[str]] = {}
|
|
427
|
+
for p in paths:
|
|
428
|
+
parts = p.replace("\\", "/").split("/")
|
|
429
|
+
for i, part in enumerate(parts):
|
|
430
|
+
if part in _DDD_LAYERS and i >= 2:
|
|
431
|
+
mod = parts[i - 1]
|
|
432
|
+
if mod in bounded_context_names:
|
|
433
|
+
module_files.setdefault(mod, []).append(p)
|
|
434
|
+
break
|
|
435
|
+
return module_files
|
|
436
|
+
|
|
417
437
|
def _is_tooling(self, path: str) -> bool:
|
|
418
438
|
norm = path.replace("\\", "/")
|
|
419
439
|
return any(norm.startswith(p) for p in _TOOLING_PREFIXES)
|
sourcecode/cli.py
CHANGED
|
@@ -790,7 +790,7 @@ def main(
|
|
|
790
790
|
# Require at least 8: src(1)+main(2)+java(3)+com(4)+co(5)+app(6)+module(7)+file.
|
|
791
791
|
_java_manifest_names = {"pom.xml", "build.gradle", "build.gradle.kts"}
|
|
792
792
|
_is_java = any(Path(m).name in _java_manifest_names for m in manifests)
|
|
793
|
-
_java_min_depth =
|
|
793
|
+
_java_min_depth = 10
|
|
794
794
|
effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
|
|
795
795
|
|
|
796
796
|
# --agent: enable signal analyzers; output via agent_view (not compact)
|
|
@@ -1376,6 +1376,12 @@ def main(
|
|
|
1376
1376
|
))
|
|
1377
1377
|
sm = _replace(sm, pipeline_trace=_trace.build_trace())
|
|
1378
1378
|
|
|
1379
|
+
# P3-B: Auto-switch to centrality ranking when DDD layout detected
|
|
1380
|
+
if (rank_by == "relevance"
|
|
1381
|
+
and sm.architecture is not None
|
|
1382
|
+
and sm.architecture.pattern == "ddd"):
|
|
1383
|
+
rank_by = "centrality"
|
|
1384
|
+
|
|
1379
1385
|
# Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
|
|
1380
1386
|
_is_contract_mode = mode in ("contract", "standard")
|
|
1381
1387
|
if _is_contract_mode:
|
|
@@ -193,6 +193,27 @@ class ConfidenceAnalyzer:
|
|
|
193
193
|
impact="low",
|
|
194
194
|
))
|
|
195
195
|
|
|
196
|
+
# ── Java test coverage gap check (P2-A) ──────────────────────────────
|
|
197
|
+
_java_all = [p for p in sm.file_paths if p.endswith(".java")]
|
|
198
|
+
_java_tests = [
|
|
199
|
+
p for p in _java_all
|
|
200
|
+
if "/test/" in p.replace("\\", "/") or "/tests/" in p.replace("\\", "/")
|
|
201
|
+
or Path(p).stem.endswith(("Test", "Tests", "IT", "Spec"))
|
|
202
|
+
]
|
|
203
|
+
_java_prod = [p for p in _java_all if p not in set(_java_tests)]
|
|
204
|
+
if _java_prod and len(_java_prod) >= 10:
|
|
205
|
+
_ratio = len(_java_tests) / len(_java_prod)
|
|
206
|
+
if _ratio < 0.05:
|
|
207
|
+
gaps.append(AnalysisGap(
|
|
208
|
+
area="testing",
|
|
209
|
+
reason=(
|
|
210
|
+
f"Backend test coverage critical: {len(_java_tests)} test files "
|
|
211
|
+
f"for {len(_java_prod)} Java files "
|
|
212
|
+
f"({_ratio:.1%})"
|
|
213
|
+
),
|
|
214
|
+
impact="high",
|
|
215
|
+
))
|
|
216
|
+
|
|
196
217
|
# ── Compute overall confidence ─────────────────────────────────────────
|
|
197
218
|
# Stack: use best manifest-detected stack, fall back to min
|
|
198
219
|
manifest_stacks = [s for s in sm.stacks if s.detection_method != "heuristic"]
|
sourcecode/detectors/java.py
CHANGED
|
@@ -25,8 +25,18 @@ _CONTROLLER_ADVICE_RE = re.compile(r'@ControllerAdvice\b')
|
|
|
25
25
|
_WEB_FILTER_RE = re.compile(r'@WebFilter\b')
|
|
26
26
|
_FILTER_BEAN_RE = re.compile(r'FilterRegistrationBean\b')
|
|
27
27
|
# Extracts path from @RequestMapping("/v1/foo"), @GetMapping("/bar"), etc.
|
|
28
|
+
# Handles attribute order: value= may come after method= in legacy @RequestMapping style.
|
|
28
29
|
_HTTP_PATH_RE = re.compile(
|
|
29
|
-
r'@(?:Request|Get|Post|Put|Delete|Patch)Mapping\s*\(
|
|
30
|
+
r'@(?:Request|Get|Post|Put|Delete|Patch)Mapping\s*\([^)]*?(?:value\s*=\s*)?["\']([^"\']+)["\']'
|
|
31
|
+
)
|
|
32
|
+
_REQUEST_METHOD_VERB_RE = re.compile(
|
|
33
|
+
r'method\s*=\s*RequestMethod\.([A-Z]+)'
|
|
34
|
+
)
|
|
35
|
+
# @M3FiltroSeguridad custom security annotation
|
|
36
|
+
_M3_FILTRO_RE = re.compile(r'@M3FiltroSeguridad\b')
|
|
37
|
+
_M3_FILTRO_PARAMS_RE = re.compile(
|
|
38
|
+
r'@M3FiltroSeguridad\s*\(\s*(?:nombreRecurso\s*=\s*"([^"]*)")?'
|
|
39
|
+
r'(?:[^)]*nivelRequerido\s*=\s*(\d+))?'
|
|
30
40
|
)
|
|
31
41
|
|
|
32
42
|
|
|
@@ -149,16 +159,29 @@ class JavaDetector(AbstractDetector):
|
|
|
149
159
|
|
|
150
160
|
# Quick pre-filter before running regexes
|
|
151
161
|
if ("Controller" not in content and "Filter" not in content
|
|
152
|
-
and "ControllerAdvice" not in content
|
|
162
|
+
and "ControllerAdvice" not in content
|
|
163
|
+
and "M3FiltroSeguridad" not in content):
|
|
153
164
|
return []
|
|
154
165
|
|
|
155
166
|
if _REST_CONTROLLER_RE.search(content):
|
|
156
167
|
http_path_match = _HTTP_PATH_RE.search(content)
|
|
157
168
|
http_path = http_path_match.group(1) if http_path_match else None
|
|
169
|
+
verb_match = _REQUEST_METHOD_VERB_RE.search(content)
|
|
170
|
+
if verb_match and http_path:
|
|
171
|
+
http_path = f"[{verb_match.group(1)}] {http_path}"
|
|
172
|
+
elif verb_match:
|
|
173
|
+
http_path = f"[{verb_match.group(1)}]"
|
|
174
|
+
security_evidence = None
|
|
175
|
+
m3_match = _M3_FILTRO_PARAMS_RE.search(content)
|
|
176
|
+
if m3_match:
|
|
177
|
+
nombre = m3_match.group(1) or ""
|
|
178
|
+
nivel = m3_match.group(2) or ""
|
|
179
|
+
security_evidence = f"@M3FiltroSeguridad(nombreRecurso={nombre!r}, nivelRequerido={nivel})"
|
|
158
180
|
return [EntryPoint(
|
|
159
181
|
path=rel_path, stack="java", kind="rest_controller",
|
|
160
182
|
source="annotation", confidence="high",
|
|
161
183
|
http_path=http_path,
|
|
184
|
+
evidence=security_evidence,
|
|
162
185
|
)]
|
|
163
186
|
if _CONTROLLER_ADVICE_RE.search(content):
|
|
164
187
|
return [EntryPoint(
|
|
@@ -168,10 +191,22 @@ class JavaDetector(AbstractDetector):
|
|
|
168
191
|
if _MVC_CONTROLLER_RE.search(content) and _REQUEST_MAPPING_RE.search(content):
|
|
169
192
|
http_path_match = _HTTP_PATH_RE.search(content)
|
|
170
193
|
http_path = http_path_match.group(1) if http_path_match else None
|
|
194
|
+
verb_match = _REQUEST_METHOD_VERB_RE.search(content)
|
|
195
|
+
if verb_match and http_path:
|
|
196
|
+
http_path = f"[{verb_match.group(1)}] {http_path}"
|
|
197
|
+
elif verb_match:
|
|
198
|
+
http_path = f"[{verb_match.group(1)}]"
|
|
199
|
+
security_evidence = None
|
|
200
|
+
m3_match = _M3_FILTRO_PARAMS_RE.search(content)
|
|
201
|
+
if m3_match:
|
|
202
|
+
nombre = m3_match.group(1) or ""
|
|
203
|
+
nivel = m3_match.group(2) or ""
|
|
204
|
+
security_evidence = f"@M3FiltroSeguridad(nombreRecurso={nombre!r}, nivelRequerido={nivel})"
|
|
171
205
|
return [EntryPoint(
|
|
172
206
|
path=rel_path, stack="java", kind="mvc_controller",
|
|
173
207
|
source="annotation", confidence="medium",
|
|
174
208
|
http_path=http_path,
|
|
209
|
+
evidence=security_evidence,
|
|
175
210
|
)]
|
|
176
211
|
if _WEB_FILTER_RE.search(content):
|
|
177
212
|
return [EntryPoint(
|
sourcecode/env_analyzer.py
CHANGED
|
@@ -27,6 +27,8 @@ _ENV_EXAMPLE_NAMES = {
|
|
|
27
27
|
|
|
28
28
|
# Spring Boot application.properties / application.yml and their profile variants
|
|
29
29
|
_SPRING_CONF_BASE = {"application.properties", "application.yml", "application.yaml"}
|
|
30
|
+
# Matches options/{profile}/ in multi-tenant SAS layout paths
|
|
31
|
+
_OPTIONS_PROFILE_PATH_RE = re.compile(r'options/([a-z0-9_-]+)/', re.IGNORECASE)
|
|
30
32
|
_SPRING_CONF_PROFILE_RE = re.compile(r'^application-([a-z0-9_-]+)\.(properties|ya?ml)$', re.IGNORECASE)
|
|
31
33
|
# Matches ${ENV_VAR} or ${ENV_VAR:default} where ENV_VAR is UPPER_SNAKE_CASE.
|
|
32
34
|
# Group 1 = key, Group 2 = default (may be empty string, absent = no default).
|
|
@@ -507,6 +509,10 @@ class EnvAnalyzer:
|
|
|
507
509
|
# Spring Boot application.properties / application.yml (incl. profiles)
|
|
508
510
|
if name_lower in _SPRING_CONF_BASE or _SPRING_CONF_PROFILE_RE.match(name_lower):
|
|
509
511
|
profile = _extract_spring_profile(name)
|
|
512
|
+
# Override profile if path contains options/{profile}/ (multi-tenant SAS layout)
|
|
513
|
+
path_profile_match = _OPTIONS_PROFILE_PATH_RE.search(rel)
|
|
514
|
+
if path_profile_match:
|
|
515
|
+
profile = path_profile_match.group(1)
|
|
510
516
|
if profile and profile not in profiles_scanned:
|
|
511
517
|
profiles_scanned.append(profile)
|
|
512
518
|
count = _parse_spring_config(entry, rel, findings, profile)
|
sourcecode/metrics_analyzer.py
CHANGED
|
@@ -229,6 +229,29 @@ class MetricsAnalyzer:
|
|
|
229
229
|
"null complexity fields are expected, not an error."
|
|
230
230
|
)
|
|
231
231
|
|
|
232
|
+
# P2-C: DDD module metrics — group by module, count files/methods per layer
|
|
233
|
+
_DDD_LAYERS = {"domain", "application", "infrastructure"}
|
|
234
|
+
ddd_files = [r for r in records if "/ddd/" in r.path.replace("\\", "/")]
|
|
235
|
+
if ddd_files:
|
|
236
|
+
module_layer_data: dict[str, dict[str, dict]] = {}
|
|
237
|
+
for fm in ddd_files:
|
|
238
|
+
parts = fm.path.replace("\\", "/").split("/")
|
|
239
|
+
for i, part in enumerate(parts):
|
|
240
|
+
if part in _DDD_LAYERS and i >= 2:
|
|
241
|
+
module = parts[i - 1]
|
|
242
|
+
layer = part
|
|
243
|
+
if module not in module_layer_data:
|
|
244
|
+
module_layer_data[module] = {lyr: {"files": 0, "methods": 0} for lyr in _DDD_LAYERS}
|
|
245
|
+
module_layer_data[module][layer]["files"] += 1
|
|
246
|
+
module_layer_data[module][layer]["methods"] += fm.function_count or 0
|
|
247
|
+
break
|
|
248
|
+
ddd_metrics = [
|
|
249
|
+
{"module": mod, "layers": layers}
|
|
250
|
+
for mod, layers in sorted(module_layer_data.items())
|
|
251
|
+
]
|
|
252
|
+
else:
|
|
253
|
+
ddd_metrics = []
|
|
254
|
+
|
|
232
255
|
summary = MetricsSummary(
|
|
233
256
|
requested=True,
|
|
234
257
|
file_count=len(records),
|
|
@@ -238,6 +261,7 @@ class MetricsAnalyzer:
|
|
|
238
261
|
coverage_records=coverage_records,
|
|
239
262
|
coverage_sources_found=sorted({r.format for r in coverage_records}),
|
|
240
263
|
limitations=limitations,
|
|
264
|
+
ddd_module_metrics=ddd_metrics,
|
|
241
265
|
)
|
|
242
266
|
return records, summary
|
|
243
267
|
|
sourcecode/schema.py
CHANGED
|
@@ -225,6 +225,7 @@ class MetricsSummary:
|
|
|
225
225
|
coverage_records: list[CoverageRecord] = field(default_factory=list)
|
|
226
226
|
coverage_sources_found: list[str] = field(default_factory=list)
|
|
227
227
|
limitations: list[str] = field(default_factory=list)
|
|
228
|
+
ddd_module_metrics: list[dict] = field(default_factory=list)
|
|
228
229
|
|
|
229
230
|
|
|
230
231
|
@dataclass
|
sourcecode/semantic_analyzer.py
CHANGED
|
@@ -40,6 +40,34 @@ _MAX_SYMBOLS = 10_000
|
|
|
40
40
|
# JS/TS keyword and builtin exclusions (Plan 12-03)
|
|
41
41
|
# ---------------------------------------------------------------------------
|
|
42
42
|
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Java/JVM heuristic regex constants (module-level for performance)
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
_AUTOWIRED_FIELD_RE = re.compile(
|
|
48
|
+
r'@Autowired\s+(?:private\s+|protected\s+|public\s+)?([A-Z][A-Za-z0-9_<>]*)\s+(\w+)\s*;'
|
|
49
|
+
)
|
|
50
|
+
_MAPPER_IFACE_RE = re.compile(
|
|
51
|
+
r'@Mapper\b.*?(?:public\s+)?interface\s+([A-Z][A-Za-z0-9_]*)',
|
|
52
|
+
re.DOTALL,
|
|
53
|
+
)
|
|
54
|
+
_EXTENDS_RE = re.compile(
|
|
55
|
+
r'(?:class|interface)\s+([A-Z][A-Za-z0-9_]*)\s+extends\s+([A-Z][A-Za-z0-9_]*)'
|
|
56
|
+
)
|
|
57
|
+
_M3_FILTRO_METHOD_RE = re.compile(
|
|
58
|
+
r'@M3FiltroSeguridad(?:\([^)]*\))?\s+(?:@[^\s]+\s+)*'
|
|
59
|
+
r'(?:public|private|protected)\s+\w[\w<>\[\]]*\s+([a-z][A-Za-z0-9_]*)\s*\('
|
|
60
|
+
)
|
|
61
|
+
_LOMBOK_CLASS_RE = re.compile(
|
|
62
|
+
r'(@(?:Data|Slf4j|Builder|AllArgsConstructor|NoArgsConstructor)(?:\([^)]*\))?\s+)*'
|
|
63
|
+
r'(?:public\s+)?(?:class|interface)\s+([A-Z][A-Za-z0-9_]*)',
|
|
64
|
+
re.MULTILINE,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
# JS/TS keyword and builtin exclusions (Plan 12-03)
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
|
|
43
71
|
_JS_KEYWORD_EXCLUSIONS: frozenset[str] = frozenset({
|
|
44
72
|
# JS reserved words
|
|
45
73
|
"if", "else", "for", "while", "do", "switch", "case", "break", "continue",
|
|
@@ -645,6 +673,29 @@ class SemanticAnalyzer:
|
|
|
645
673
|
),
|
|
646
674
|
}
|
|
647
675
|
|
|
676
|
+
# P1-D: Link @Mapper interfaces to their *Mapper.xml files
|
|
677
|
+
xml_paths = [p for p in all_paths if p.endswith(("Mapper.xml", "MyBatis.xml"))]
|
|
678
|
+
mapper_xml_index: dict[str, str] = {}
|
|
679
|
+
for xp in xml_paths:
|
|
680
|
+
stem = Path(xp).stem # e.g. "PacienteMapper"
|
|
681
|
+
mapper_xml_index[stem] = xp
|
|
682
|
+
|
|
683
|
+
for sym in all_symbols:
|
|
684
|
+
if sym.kind == "mapper_interface" and sym.language == "java":
|
|
685
|
+
xml_key = sym.symbol
|
|
686
|
+
if xml_key in mapper_xml_index:
|
|
687
|
+
xml_path = mapper_xml_index[xml_key]
|
|
688
|
+
links.append(SymbolLink(
|
|
689
|
+
importer_path=Path(sym.path).as_posix(),
|
|
690
|
+
symbol=sym.symbol,
|
|
691
|
+
source_path=xml_path,
|
|
692
|
+
source_line=None,
|
|
693
|
+
is_external=False,
|
|
694
|
+
confidence="high",
|
|
695
|
+
method="heuristic",
|
|
696
|
+
workspace=workspace,
|
|
697
|
+
))
|
|
698
|
+
|
|
648
699
|
# Determine explicit analysis status — never emit silent empty results.
|
|
649
700
|
# An agent must be able to tell "analysis ran and found nothing" from
|
|
650
701
|
# "analysis failed to run" or "significant coverage gap".
|
|
@@ -865,6 +916,8 @@ class SemanticAnalyzer:
|
|
|
865
916
|
"""Heuristic Java/Kotlin: detecta class/method declarations y call sites.
|
|
866
917
|
|
|
867
918
|
method="heuristic", confidence="low" para todos los edges Java.
|
|
919
|
+
Includes: Lombok synthetic symbols, @Autowired field edges,
|
|
920
|
+
@Mapper interface detection, inheritance chains, @M3FiltroSeguridad AOP edges.
|
|
868
921
|
"""
|
|
869
922
|
_JAVA_KEYWORDS: frozenset[str] = frozenset({
|
|
870
923
|
"if", "for", "while", "switch", "catch", "super", "this", "new",
|
|
@@ -923,6 +976,105 @@ class SemanticAnalyzer:
|
|
|
923
976
|
method="heuristic",
|
|
924
977
|
))
|
|
925
978
|
|
|
979
|
+
# P1-D: @Mapper interface detection — emit as mapper_interface symbol
|
|
980
|
+
for m in _MAPPER_IFACE_RE.finditer(content):
|
|
981
|
+
iface_name = m.group(1)
|
|
982
|
+
line = content[: m.start()].count("\n") + 1
|
|
983
|
+
symbols.append(SymbolRecord(
|
|
984
|
+
symbol=iface_name,
|
|
985
|
+
kind="mapper_interface",
|
|
986
|
+
language="java",
|
|
987
|
+
path=rel_path,
|
|
988
|
+
line=line,
|
|
989
|
+
exported=True,
|
|
990
|
+
))
|
|
991
|
+
|
|
992
|
+
# P1-E: Inheritance chain — class X extends Y → edge X→Y
|
|
993
|
+
for m in _EXTENDS_RE.finditer(content):
|
|
994
|
+
subclass = m.group(1)
|
|
995
|
+
superclass = m.group(2)
|
|
996
|
+
line = content[: m.start()].count("\n") + 1
|
|
997
|
+
calls.append(CallRecord(
|
|
998
|
+
caller_path=rel_path,
|
|
999
|
+
caller_symbol=subclass,
|
|
1000
|
+
callee_path=rel_path,
|
|
1001
|
+
callee_symbol=superclass,
|
|
1002
|
+
call_line=line,
|
|
1003
|
+
confidence="low",
|
|
1004
|
+
method="heuristic",
|
|
1005
|
+
))
|
|
1006
|
+
|
|
1007
|
+
# P1-F: Lombok annotations — emit synthetic symbols
|
|
1008
|
+
for m in _LOMBOK_CLASS_RE.finditer(content):
|
|
1009
|
+
annotations_block = m.group(0)
|
|
1010
|
+
class_name = m.group(2)
|
|
1011
|
+
line = content[: m.start()].count("\n") + 1
|
|
1012
|
+
if "@Slf4j" in annotations_block:
|
|
1013
|
+
symbols.append(SymbolRecord(
|
|
1014
|
+
symbol="log",
|
|
1015
|
+
kind="field",
|
|
1016
|
+
language="java",
|
|
1017
|
+
path=rel_path,
|
|
1018
|
+
line=line,
|
|
1019
|
+
exported=False,
|
|
1020
|
+
))
|
|
1021
|
+
if "@Builder" in annotations_block:
|
|
1022
|
+
symbols.append(SymbolRecord(
|
|
1023
|
+
symbol=f"{class_name}.builder",
|
|
1024
|
+
kind="function",
|
|
1025
|
+
language="java",
|
|
1026
|
+
path=rel_path,
|
|
1027
|
+
line=line,
|
|
1028
|
+
exported=True,
|
|
1029
|
+
))
|
|
1030
|
+
if "@AllArgsConstructor" in annotations_block or "@Data" in annotations_block:
|
|
1031
|
+
symbols.append(SymbolRecord(
|
|
1032
|
+
symbol=f"{class_name}(allArgs)",
|
|
1033
|
+
kind="function",
|
|
1034
|
+
language="java",
|
|
1035
|
+
path=rel_path,
|
|
1036
|
+
line=line,
|
|
1037
|
+
exported=True,
|
|
1038
|
+
))
|
|
1039
|
+
if "@NoArgsConstructor" in annotations_block:
|
|
1040
|
+
symbols.append(SymbolRecord(
|
|
1041
|
+
symbol=f"{class_name}()",
|
|
1042
|
+
kind="function",
|
|
1043
|
+
language="java",
|
|
1044
|
+
path=rel_path,
|
|
1045
|
+
line=line,
|
|
1046
|
+
exported=True,
|
|
1047
|
+
))
|
|
1048
|
+
|
|
1049
|
+
# P1-G: @Autowired field injection — emit dependency edges
|
|
1050
|
+
for m in _AUTOWIRED_FIELD_RE.finditer(content):
|
|
1051
|
+
field_type = m.group(1)
|
|
1052
|
+
field_name = m.group(2)
|
|
1053
|
+
line = content[: m.start()].count("\n") + 1
|
|
1054
|
+
calls.append(CallRecord(
|
|
1055
|
+
caller_path=rel_path,
|
|
1056
|
+
caller_symbol=field_name,
|
|
1057
|
+
callee_path=rel_path,
|
|
1058
|
+
callee_symbol=field_type,
|
|
1059
|
+
call_line=line,
|
|
1060
|
+
confidence="medium",
|
|
1061
|
+
method="heuristic",
|
|
1062
|
+
))
|
|
1063
|
+
|
|
1064
|
+
# P3-C: @M3FiltroSeguridad AOP proxy edges
|
|
1065
|
+
for m in _M3_FILTRO_METHOD_RE.finditer(content):
|
|
1066
|
+
method_name = m.group(1)
|
|
1067
|
+
line = content[: m.start()].count("\n") + 1
|
|
1068
|
+
calls.append(CallRecord(
|
|
1069
|
+
caller_path=rel_path,
|
|
1070
|
+
caller_symbol="M3FiltroSeguridadImpl",
|
|
1071
|
+
callee_path=rel_path,
|
|
1072
|
+
callee_symbol=method_name,
|
|
1073
|
+
call_line=line,
|
|
1074
|
+
confidence="medium",
|
|
1075
|
+
method="heuristic",
|
|
1076
|
+
))
|
|
1077
|
+
|
|
926
1078
|
return symbols, calls
|
|
927
1079
|
|
|
928
1080
|
# -----------------------------------------------------------------------
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=LnE1zIwm5EchnSw2jva5dUiw_jkGB75astJjLW5Lh-Y,102
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=6dh34C2qZXyRbw-8xBhbEwDdXanM6CRFRWayVoYITnA,10190
|
|
3
|
-
sourcecode/architecture_analyzer.py,sha256=
|
|
3
|
+
sourcecode/architecture_analyzer.py,sha256=qzDW3_lQv__czQ-qs6AqEEoMvTfhfp7M7kNslPuQy7A,32128
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=J9yoLgh8wXwIRrT6q6JooB6PekivbOEYpJz4BUXdalk,20545
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=0OHQwTUBBc9lmqPLryVeB1z8dGIC6NhLlar800CD9oI,41129
|
|
6
6
|
sourcecode/classifier.py,sha256=GKTMN8qKZX7ponSwDJfN08RrasI4CVpq1_gFBgEopps,7093
|
|
7
|
-
sourcecode/cli.py,sha256=
|
|
7
|
+
sourcecode/cli.py,sha256=bWp3iQI_x-DF8zaw151q-Q6fRbEuCbJmuUSBW8LoRSY,72252
|
|
8
8
|
sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
|
|
9
|
-
sourcecode/confidence_analyzer.py,sha256=
|
|
9
|
+
sourcecode/confidence_analyzer.py,sha256=HcaewB2pZaZ_hfKrZWtr_yPMY2-CxS1zzTUD7c4argc,13188
|
|
10
10
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
11
11
|
sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
|
|
12
12
|
sourcecode/contract_model.py,sha256=gCf9-Kj0G7l0lvRTAcRfFAfMgs1Rpizv4mKovQLYUkw,3434
|
|
@@ -15,11 +15,11 @@ sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo
|
|
|
15
15
|
sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJcRhB8,52757
|
|
16
16
|
sourcecode/doc_analyzer.py,sha256=TttdS7mndKQhyJCfJnnAsyGCJrf-TIL7oXxDlTLUFKE,21248
|
|
17
17
|
sourcecode/entrypoint_classifier.py,sha256=a69dMGyxCTd_LOm3oqj-EXWpRmbmeujN7T1mr2eJ1as,3877
|
|
18
|
-
sourcecode/env_analyzer.py,sha256=
|
|
18
|
+
sourcecode/env_analyzer.py,sha256=GxCidahAAIptTdDFIlVB6URd4HBnBlIX_SqUov3MBRQ,22076
|
|
19
19
|
sourcecode/file_classifier.py,sha256=48ly5Z6exkzBy8lNy1AkdP4-oJqIA1zT3LZfffuTyDo,11572
|
|
20
20
|
sourcecode/git_analyzer.py,sha256=PD3eNWydznQ6KLNpxGzBqizIHoPIKevfwz9Xyf_pDt4,11600
|
|
21
21
|
sourcecode/graph_analyzer.py,sha256=hMOsLLz9B0UnQ4xwbHdgr3bFvqpw0bQ8kN-xmEn3Krk,64156
|
|
22
|
-
sourcecode/metrics_analyzer.py,sha256=
|
|
22
|
+
sourcecode/metrics_analyzer.py,sha256=FGaHQaEdsSlShlV7lZHOFHbdaVU0z84zeQxljWy5kfI,22018
|
|
23
23
|
sourcecode/prepare_context.py,sha256=FKh-M5B74r-yztuAgfuSE8RjIZvsq9YRwTr74zmldxI,35901
|
|
24
24
|
sourcecode/ranking_engine.py,sha256=virVglafZufioHpZpwktjMvUiL0TZELWQCQnQNV8dFo,9360
|
|
25
25
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
@@ -27,8 +27,8 @@ sourcecode/relevance_scorer.py,sha256=MYF4FFkveAQps9SmTeTlh6ODiBz2F--_hWNeHMLtUH
|
|
|
27
27
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
28
28
|
sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBGPjQ,14792
|
|
29
29
|
sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
|
|
30
|
-
sourcecode/schema.py,sha256=
|
|
31
|
-
sourcecode/semantic_analyzer.py,sha256=
|
|
30
|
+
sourcecode/schema.py,sha256=M0aA3sIIm8QHDIAducZUm5mGpgJ7oXfqtyvoMkLGMac,23270
|
|
31
|
+
sourcecode/semantic_analyzer.py,sha256=12TwXYkYbDcBdu0heX_EmfPM2EkO8a_r5osf0SaeQbs,88956
|
|
32
32
|
sourcecode/serializer.py,sha256=k-rddaaIlvAA5F2qvizCh_yd4oAlhhsg2obrYoJKtlo,65424
|
|
33
33
|
sourcecode/summarizer.py,sha256=ZuzIdm3t8A-d5MuQL0TSNLrd-L0IQIuguIxeNXMNJf8,16070
|
|
34
34
|
sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
|
|
@@ -42,7 +42,7 @@ sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJ
|
|
|
42
42
|
sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
|
|
43
43
|
sourcecode/detectors/heuristic.py,sha256=bCqqgbHavl4Sse3dqT8mwmo1wAdgeJr7VyXOmfClLKo,3387
|
|
44
44
|
sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
|
|
45
|
-
sourcecode/detectors/java.py,sha256=
|
|
45
|
+
sourcecode/detectors/java.py,sha256=Lv-a1YeXezJYZCJduWfeeLMTUgyPz4lyiYu9Edi8-7E,10821
|
|
46
46
|
sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
|
|
47
47
|
sourcecode/detectors/nodejs.py,sha256=7fsyAmrGkkguX6U80HUQpIe9MRaYyi_A7zbaRtmFmGc,13097
|
|
48
48
|
sourcecode/detectors/parsers.py,sha256=ugPg8yNUf0Ai1gA7Fnn6wAkYGFjTxRodSP3IeViYJJ4,2290
|
|
@@ -60,8 +60,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
60
60
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
61
61
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
62
62
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
63
|
-
sourcecode-1.
|
|
64
|
-
sourcecode-1.
|
|
65
|
-
sourcecode-1.
|
|
66
|
-
sourcecode-1.
|
|
67
|
-
sourcecode-1.
|
|
63
|
+
sourcecode-1.2.0.dist-info/METADATA,sha256=Tty3oliUz4DgbBeOK4aK9QnsT5qKCefW1f0q-XbFwdE,20411
|
|
64
|
+
sourcecode-1.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
65
|
+
sourcecode-1.2.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
66
|
+
sourcecode-1.2.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
67
|
+
sourcecode-1.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|