Generator: pilot analysis and fixes; G0002-G0021 tasks; handover notes merged

- Static bundle checks (name length, seed type, reserved words, contract test classes,
  testclasses_file), local abaplint parser check before SAP, max_tokens, robust JSON parse
- Runner: G2 for CDS views with parameters, contract_check detail in report
- G0020 and G0013 fixed by review and revalidated (oracle 100, null 0)
- Pilot results in docs/faz1-tasarim.md 11e; handover notes merged into CLAUDE.md and docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-03 05:04:23 +02:00
parent 15eb9f1bb2
commit 5de5e2851c
162 changed files with 9943 additions and 23 deletions

View File

@@ -23,6 +23,22 @@ DELETE_ORDER = ["CLAS", "INTF", "PROG", "FUNC", "FUGR", "SRVD", "DDLX", "DCLS",
SOURCE_TYPES = ("CLAS", "INTF", "PROG", "FUNC", "DDLS", "DCLS", "DDLX", "TABL")
def _cds_elements(src):
"""Element names of a CDS view select list (alias, else last path component). Used when a
SELECT on the view is not possible, for example a view with parameters."""
body = re.sub(r"//[^\n]*|/\*.*?\*/", "", src or "", flags=re.S)
m = re.search(r"\{(.*)\}", body, re.S)
out = set()
for el in re.split(r",(?![^()]*\))", m.group(1) if m else ""):
el = re.sub(r"@\S+(\s*:\s*('[^']*'|\S+))?", "", el).strip()
if not el or el.lower().startswith("_") and " as " not in el.lower():
continue
alias = re.search(r"\bas\s+(\w+)\s*$", el, re.I)
name = alias.group(1) if alias else re.split(r"[.\s]", el.split()[-1])[-1]
out.add(name.upper())
return out
def _obj_args(otype, name, fg=None):
a = {"objectType": otype, "objectName": name}
if fg:
@@ -222,26 +238,36 @@ class Runner:
g1.append({"name": c["name"], "ok": ok})
g["G1_active"] = all(x["ok"] for x in g1) and bool(g1)
g2 = True
g2_detail = []
for c in contract:
src = sources.get((c["type"], c["name"].upper()), "")
if c.get("implements") and not re.search(rf"INTERFACES\s+{re.escape(c['implements'])}\b", src, re.I):
g2 = False
g2_detail.append(f"{c['name']}: does not implement {c['implements']}")
if c["type"] == "FUNC": # signature: every parameter with its type in the FUNCTION header
header = src.split(".", 1)[0]
for prm in c.get("params", []):
if not re.search(rf"\b{re.escape(prm['name'])}\b\)?\s+TYPE\s+{re.escape(prm['type'])}\b",
header, re.I):
g2 = False
g2_detail.append(f"{c['name']}: no parameter {prm['name']} TYPE {prm['type']} "
"in the FUNCTION header")
if c["type"] == "PROG": # selection screen parameters
for prm in c.get("parameters", []):
if not re.search(rf"(PARAMETERS|SELECT-OPTIONS)\s*:?[^.]*\b{re.escape(prm)}\b", src, re.I):
g2 = False
g2_detail.append(f"{c['name']}: no selection screen parameter {prm}")
if c["type"] == "DDLS" and c.get("fields") and g["G1_active"]:
_, q = mcp.call("sap_sql_query", {"query": f"SELECT * FROM {c['name']}", "maxRows": 1})
cols = {col.get("name", "").upper() for col in (_json(q) or {}).get("columns", [])}
if not {f.upper() for f in c["fields"]} <= cols:
if not cols: # view with parameters: SELECT without parameters fails
cols = _cds_elements(src)
missing = {f.upper() for f in c["fields"]} - cols
if missing:
g2 = False
g2_detail.append(f"{c['name']}: CDS elements missing: {sorted(missing)}")
g["G2_contract"] = g2 and g["G1_active"]
rep["contract_check"] = {"g1": g1, "g2": g2_detail}
g["G4_out_of_scope"] = all(
_norm(self._source(mcp, o["type"], o["name"], o.get("functionGroup")))
== _norm(seed_src[o["name"].upper()])