STRU and exception tasks accepted; G6 cascade fix, exception class mutants, RTTI unit notes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-05 19:25:09 +02:00
parent b119f1afac
commit 55f4068330
36 changed files with 5380 additions and 34 deletions

View File

@@ -74,7 +74,7 @@ needs an amount or a quantity, the reference field needs a reference annotation
abap.cuky in the same table; @Semantics.quantity.unitOfMeasure : '{{p}}ord.unit' with unit : abap.unit(3)); a
reference without the table name fails with "Annotation with reference to unit code ... is uncomplete". Reference: the DDL file and ONE global own-test class
(type CLAS). Hidden tests (one global class): RTTI on the table: cast cl_abap_typedescr=>describe_by_name( '{{P}}ORD' ) to
cl_abap_structdescr and check get_ddic_field_list( ) (field names, key flags, lengths, decimals, types), and a
cl_abap_structdescr and check get_ddic_field_list( ) (field names, key flags, LENG in characters, decimals, types; cl_abap_elemdescr->length is in BYTES: 2 per character), and a
cl_osql_test_environment test (create( i_dependency_list = VALUE #( ( '{{P}}ORD' ) ) ), insert, select back; a second
INSERT with the same key gives sy-subrc = 4). No seed is needed.""",
"STRU": """The model must create a DDIC STRUCTURE (type STRU) as the main contract object. DDL source: annotations
@@ -83,7 +83,9 @@ INSERT with the same key gives sy-subrc = 4). No seed is needed.""",
"include {{p}}other;" when the spec asks). Contract entry: {"type": "STRU", "name": "{{P}}NAME", "fields": [...]}
(the field names in lower case). The Business rules list every field with data type and length. Reference: the DDL file
and ONE global own-test class (type CLAS). Hidden tests (one global class): RTTI: cl_abap_typedescr=>describe_by_name(
'{{P}}NAME' ) cast to cl_abap_structdescr; check components (names, length, decimals, type kind) and the order.""",
'{{P}}NAME' ) cast to cl_abap_structdescr; check components (names, length, decimals, type kind) and the order. RTTI units: cl_abap_elemdescr->length is in BYTES
(2 bytes per character in Unicode), so a char(10) field has length 20; compare characters with output_length or with
LENG of cl_abap_structdescr=>get_ddic_field_list( ) (characters), and decimals with ->decimals.""",
"MSAG": """The model must create a MESSAGE CLASS (type MSAG) as the main contract object. The name has at most 20
characters including the prefix. A message class has NO source file. In task.json the reference entry has no "file":
{"type": "MSAG", "name": "{{P}}MSG", "description": "...", "messages": [{"msgno": "001", "text": "Order &1 is blocked"}, ...]}

View File

@@ -169,6 +169,8 @@ def decl_mutants(src, otype, seed, n=MAX_MUTANTS):
for m in re.finditer(r"^(\s*)key(\s+)(?!client\b)(\w+\s*:)", src, re.I | re.M):
add("key", m, f"{m.group(1)}{m.group(3)}")
if otype == "INTF":
for m in re.finditer(r"(\bmsgno\s*=\s*')(\d+)(')", src, re.I): # message number of an exception text id
add("msgno", m, f"{m.group(1)}{str(int(m.group(2)) + 1).zfill(len(m.group(2)))}{m.group(3)}")
for m in re.finditer(r"(\bVALUE\s+)(\d+)(?=\s*\.)", src, re.I):
add("const", m, f"{m.group(1)}{int(m.group(2)) + 1}")
for m in re.finditer(r"(\bVALUE\s+)'([^'\n]{1,20})'", src, re.I):
@@ -236,7 +238,10 @@ def check_task(pool_root, task_id, run_base, n=MAX_MUTANTS, keep=False):
src = open(os.path.join(task_dir, o["file"])).read()
if o["type"] in ("TABL", "STRU", "INTF"):
return decl_mutants(src, o["type"], f"{task_id}:{o['name']}", n)
return mutants(src, o["type"], f"{task_id}:{o['name']}", n)
ms = mutants(src, o["type"], f"{task_id}:{o['name']}", n)
if o["type"] == "CLAS" and len(ms) < n: # exception classes: constants, message numbers, texts
ms += decl_mutants(src, "INTF", f"{task_id}:{o['name']}", n - len(ms))
return ms
cand = [[(o, d, m) for d, m in _muts(o)] for o in targets]
plan = []
while len(plan) < n and any(cand):

View File

@@ -426,8 +426,15 @@ class Runner:
contract_files = {f"{c['name'].lower()}.{c['type'].lower()}.abap" for c in contract}
# abaplint does not know standard superclasses (CX_STATIC_CHECK ...); an exception hierarchy
# then gives "Super class ... not found or contains errors". A4H activation (G1) covers this.
# Cascade: a class that uses such an exception class gets "Definition for X not found in scope" (X is
# the exception class abaplint could not build). Messages that name a broken class are not counted.
broken = {re.sub(r"\.(clas|intf)\.abap$", "", i["file"]).upper() for i in issues
if re.match(r"Super class .* not found or contains errors", i["msg"] or "")}
def _cascade(msg):
return any(re.search(rf"\b{re.escape(b)}\b", msg or "", re.I) for b in broken)
g["G6_release"] = not any(i["rule"] == "check_syntax" and i["file"] in contract_files
and not re.match(r"Super class .* not found or contains errors", i["msg"] or "")
and not _cascade(i["msg"])
for i in issues)
# 8 score