From sap-gen-code
Validates ABAP source code quality before deployment. Checks: (1) Variable naming conventions against shared rules file, (2) Data type validity via SAP Dictionary (DDIF_FIELDINFO_GET / DDIF_DTEL_GET), (3) Unused variable detection, (4) SQL field validation — checks SELECT/UPDATE/DELETE field names against table definitions. Writes a tab-delimited result file with fix advice for each issue. SAP connection is optional — offline mode skips type and SQL field validation. Prerequisites: SAP GUI installed (provides SAP.Functions 32-bit COM object).
How this skill is triggered — by the user, by Claude, or both
Slash command
/sap-gen-code:sap-check-abap <path-to-abap-source-file><path-to-abap-source-file>The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You validate ABAP source code quality before deployment. You check variable naming conventions against a shared rules file, validate data types against the SAP Dictionary, detect unused variables, and verify SQL field references against table definitions. Results are written to a tab-delimited file with fix advice.
You validate ABAP source code quality before deployment. You check variable naming conventions against a shared rules file, validate data types against the SAP Dictionary, detect unused variables, and verify SQL field references against table definitions. Results are written to a tab-delimited file with fix advice.
Task: $ARGUMENTS
Settings reads/writes follow <SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md — merge settings.local.json over settings.json per-key on the .value field; writes always go to settings.local.json. Resolve cross-plugin paths: 3 levels up from <SKILL_DIR>, then into sap-dev-core\settings.json and (if present) sap-dev-core\settings.local.json. Read work_dir, custom_url.
| Setting | Default if blank |
|---|---|
work_dir | C:\sap_dev_work |
custom_url | {work_dir}\custom |
Set {WORK_TEMP} = {work_dir}\temp
Ensure the temp directory exists:
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
Start a structured log run. State file: {WORK_TEMP}\sap_check_abap_run.json. Best-effort.
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{WORK_TEMP}\sap_check_abap_run.json" -Skill sap-check-abap -ParamsJson "{\"abap_file\":\"<ABAP_FILE>\"}"
Extract from $ARGUMENTS:
Verify the source file exists:
powershell -Command "if (Test-Path 'THE_FILE_PATH') { 'EXISTS' } else { 'NOT FOUND' }"
If the file does not exist, tell the user and stop.
Set these paths:
.check.tsv extension (e.g. {WORK_TEMP}\ztest.abap → {WORK_TEMP}\ztest.abap.check.tsv){custom_url}\abap_naming_rules.tsv exists. If yes, use it. Otherwise fall back to <SAP_DEV_CORE_SHARED_DIR>\tables\abap_naming_rules.tsv — resolve <SAP_DEV_CORE_SHARED_DIR> by going 3 levels up from <SKILL_DIR> (skill → skills/ → plugin dir → plugins root), then into sap-dev-core\shared{custom_url}\sap_object_naming_rules.tsv. If yes, use it. Otherwise fall back to <SAP_DEV_CORE_SHARED_DIR>\tables\sap_object_naming_rules.tsv. Used by Step 1.5 to validate program / FORM / class / method names.Check custom overrides:
powershell -Command "if (Test-Path '{custom_url}\abap_naming_rules.tsv') { 'CUSTOM' } else { 'DEFAULT' }"
powershell -Command "if (Test-Path '{custom_url}\sap_object_naming_rules.tsv') { 'CUSTOM' } else { 'DEFAULT' }"
Parse the ABAP source for top-level object declarations and validate each name
against sap_object_naming_rules.tsv via the shared validator
<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_object_name.ps1.
Patterns to scan (case-insensitive, regex):
| ABAP construct | OBJECT_TYPE for validator |
|---|---|
^\s*REPORT\s+(\w+) or ^\s*PROGRAM\s+(\w+) | PROGRAM |
^\s*FORM\s+(\w+) | SUBROUTINE |
^\s*CLASS\s+(\w+)\s+DEFINITION(?!.*\bLOCAL\b) (skip rows containing LOCAL) | GLOBAL_CLASS |
| `^\s*(?:METHODS | CLASS-METHODS)\s+(\w+)` (inside global CLASS DEFINITION blocks only) |
For each match, call:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_object_name.ps1" -ObjectType <TYPE> -ObjectName <NAME> -CustomUrl "{custom_url}"
Exit code 1 = VIOLATION → append a row to RESULT_FILE with:
Code: OBJECT_NAMINGSeverity: WARNINGVariable: the offending nameDetail: the validator's stdout lineFix Advice: "Rename to follow <expected pattern> (e.g. <example>), or override the rule in {custom_url}\sap_object_naming_rules.tsv."Exit code 0 = OK (no row written). Exit code 2 = UNKNOWN_TYPE / RULES_NOT_FOUND → log a single INFO note and skip (do not block).
Note: variable-level naming inside FORM/METHOD/FUNCTION bodies is still handled by the existing VBScript checker (Step 3). Step 1.5 only covers the top-level object declarations.
| File | Token | Purpose |
|---|---|---|
sap-dev-core/settings.json | (config) | SAP connection parameters |
sap-dev-core/shared/tables/abap_naming_rules.tsv | %%NAMING_RULES%% | ABAP variable naming prefix conventions |
sap-dev-core/shared/tables/sap_object_naming_rules.tsv | (read by helper) | Top-level SAP object naming patterns (program / class / method / FORM). Custom override: {custom_url}\sap_object_naming_rules.tsv |
sap-dev-core/shared/scripts/sap_check_object_name.ps1 | (helper) | Shared validator invoked in Step 1.5 |
sap-dev-core/shared/rules/abap_code_quality_rules.md | (rule) | Mandatory ABAP code-quality rules — checker MUST add these as findings on top of NAMING / TYPE / SQL / UNUSED: SELECT_IN_LOOP (rule 12), FOR_ALL_ENTRIES_NO_GUARD (12), MESSAGE_E_IN_METHOD (11), MISSING_AT_HOST_VAR (13), METHOD_TOO_LONG (18), MISSING_AUTHZ_CHECK (14), STRING_CONCAT_SQL (13). Severity per rule. |
sap-dev-core/shared/templates/customer_brief.md | (config) | Project Profile — used to set the quality bar (e.g. method length limit, modern-ABAP required, ATC priority gating) |
Read SAP connection parameters from the merged sap-dev-core settings (per <SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md). The sap_password value typically comes from settings.local.json and is a dpapi:... blob — decrypt via sap_dpapi.ps1 before use.
Resolve path: go 3 levels up from <SKILL_DIR> (skill → skills/ → plugin dir → plugins root),
then into sap-dev-core\settings.json.
| Setting key | Maps to | Example |
|---|---|---|
sap_application_server | %%SAP_SERVER%% | 10.0.0.1 |
sap_system_number | %%SAP_SYSNR%% | 00 |
sap_client | %%SAP_CLIENT%% | 100 |
sap_user | %%SAP_USER%% | DEVELOPER |
sap_password | %%SAP_PASSWORD%% | (masked) |
sap_language | %%SAP_LANGUAGE%% | EN |
If sap_application_server is not configured, ask the user:
"Do you want to configure SAP connection in sap-dev-core settings.json for type validation, or run in offline mode (naming + unused checks only)?"
If offline mode, set %%SAP_SERVER%% to empty string and skip to Step 3.
The VBScript template is at ./references/sap_check_abap.vbs (relative to this skill directory).
RFC type lookups are delegated to the sidecar PowerShell helper at
<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_ddic.ps1 (uses SAP NCo 3.1).
The helper template has tokens: %%SAP_*%%, %%REQUEST_FILE%%, %%RESULT_FILE%%.
Token-substitute and write to {WORK_TEMP}\sap_checkabap_ddic_helper.ps1:
$h = Get-Content '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_ddic.ps1' -Raw
$h = $h -replace '%%SAP_SERVER%%', 'THE_SERVER'
$h = $h -replace '%%SAP_SYSNR%%', 'THE_SYSNR'
$h = $h -replace '%%SAP_CLIENT%%', 'THE_CLIENT'
$h = $h -replace '%%SAP_USER%%', 'THE_USER'
$h = $h -replace '%%SAP_PASSWORD%%', 'THE_PASSWORD'
$h = $h -replace '%%SAP_LANGUAGE%%', 'THE_LANGUAGE'
$h = $h -replace '%%RFC_LIB_PS1%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1'
$h = $h -replace '%%REQUEST_FILE%%', '{WORK_TEMP}\sap_checkabap_ddic_request.txt'
$h = $h -replace '%%RESULT_FILE%%', '{WORK_TEMP}\sap_checkabap_ddic_result.tsv'
Set-Content '{WORK_TEMP}\sap_checkabap_ddic_helper.ps1' $h -Encoding UTF8
Write {WORK_TEMP}\sap_checkabap_run.ps1:
$content = Get-Content '<SKILL_DIR>\references\sap_check_abap.vbs' -Raw
$content = $content -replace '%%SAP_SERVER%%', 'THE_SERVER'
$content = $content -replace '%%SAP_SYSNR%%', 'THE_SYSNR'
$content = $content -replace '%%SAP_CLIENT%%', 'THE_CLIENT'
$content = $content -replace '%%SAP_USER%%', 'THE_USER'
$content = $content -replace '%%SAP_PASSWORD%%', 'THE_PASSWORD'
$content = $content -replace '%%SAP_LANGUAGE%%', 'THE_LANGUAGE'
$content = $content -replace '%%ABAP_FILE%%', 'THE_ABAP_FILE'
$content = $content -replace '%%RESULT_FILE%%', 'THE_RESULT_FILE'
$content = $content -replace '%%NAMING_RULES%%', 'THE_NAMING_RULES_PATH'
$content = $content -replace '%%DDIC_HELPER_PS1%%', '{WORK_TEMP}\sap_checkabap_ddic_helper.ps1'
$content = $content -replace '%%DDIC_REQUEST_FILE%%', '{WORK_TEMP}\sap_checkabap_ddic_request.txt'
$content = $content -replace '%%DDIC_RESULT_FILE%%', '{WORK_TEMP}\sap_checkabap_ddic_result.tsv'
Set-Content '{WORK_TEMP}\sap_checkabap_run.vbs' $content -Encoding Unicode
Write-Host 'Done'
Replace all THE_* placeholders and <SKILL_DIR> / <SAP_DEV_CORE_SHARED_DIR> with absolute paths.
For offline mode, set THE_SERVER to empty string (''); the VBS will skip the helper invocation entirely.
Run:
powershell -ExecutionPolicy Bypass -File "{WORK_TEMP}\sap_checkabap_run.ps1"
Run via standard cscript (the VBS itself no longer needs 32-bit; the helper PS1 runs 32-bit PowerShell internally):
cscript.exe //NoLogo {WORK_TEMP}\sap_checkabap_run.vbs
Show the full script output as it runs. Then read the result file.
Delete the filled scripts (they contain plaintext credentials):
cmd /c del {WORK_TEMP}\sap_checkabap_run.vbs
cmd /c del {WORK_TEMP}\sap_checkabap_ddic_helper.ps1
Why: the offline parser in Step 3 can't catch two error classes that show up at SE38 upload / ATC time:
ls_clientdata-gross_wt = … where
gross_wt doesn't exist on BAPI_MARA on this S/4HANA build (was the 8
syntax errors of round 5)./sap-gen-abap already populates two cache files in the work folder when
RFC is available:
<work_folder>/_struct_signatures.txt (from Step 1.5e — DDIF_FIELDINFO_GET)<work_folder>/_authz_signatures.txt (from Step 1.5b' — RFC_READ_TABLE on AUTHX)This step runs a shared post-parser validator that reads those caches and
appends new finding rows to the same THE_RESULT_FILE the Step 3 VBS wrote.
The combined report at Step 4 then shows naming/type/SQL findings AND
struct-field/authz findings in one place.
The ABAP source lives in <work_folder>/<NAME>.abap. Both signature files
are emitted alongside it by /sap-gen-abap. Compute:
STRUCT_SIG = <directory of ABAP_FILE>\_struct_signatures.txt
AUTHZ_SIG = <directory of ABAP_FILE>\_authz_signatures.txt
If neither file exists, skip this step entirely — the upstream gen either didn't run RFC or wasn't invoked at all (e.g. operator wrote the ABAP by hand). The validator is purely additive — its absence doesn't break the existing checks.
Template at <SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_signatures.ps1.
$content = Get-Content '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_signatures.ps1' -Raw
$content = $content.Replace('%%ABAP_FILE%%', 'THE_ABAP_FILE')
$content = $content.Replace('%%STRUCT_SIG_FILE%%', 'THE_STRUCT_SIG') # may be '' if absent
$content = $content.Replace('%%AUTHZ_SIG_FILE%%', 'THE_AUTHZ_SIG') # may be '' if absent
$content = $content.Replace('%%RESULT_FILE%%', 'THE_RESULT_FILE') # SAME file Step 3 wrote
Set-Content '{WORK_TEMP}\sap_checkabap_signatures.ps1' $content -Encoding UTF8
Run via standard PowerShell (no SAP / no NCo — pure file I/O over cached TSVs):
powershell -ExecutionPolicy Bypass -File "{WORK_TEMP}\sap_checkabap_signatures.ps1"
Expected stdout (when caches are present):
INFO: Struct cache loaded — 7 struct(s) with field lists, 0 NOT_FOUND.
INFO: AUTHX cache loaded — 3 object(s), 0 NOT_FOUND.
INFO: Checked 142 struct field reference(s); 8 error(s) found.
INFO: Checked 3 AUTHORITY-CHECK statement(s); 1 error(s) found.
INFO: Appended 9 finding(s) to THE_RESULT_FILE
These are emitted by the signature validator (in addition to the codes Step 3 produces):
| Code | Severity | Meaning |
|---|---|---|
STRUCT_FIELD_MISSING | ERROR | <var>-<field> references a field that doesn't exist on <var>'s structure type (per live DDIF_FIELDINFO_GET). Fix advice points at SE11 lookup or the correct BAPI parameter (e.g. marmdata for MARM-resident fields per §22). |
STRUCT_TYPE_MISSING | ERROR | The struct type itself is in the cache with NOT_FOUND — typo or removed-in-this-release. |
AUTHZ_OBJECT_MISSING | ERROR | AUTHORITY-CHECK OBJECT '<X>' references an object the SU21 lookup returned NOT_FOUND for. |
AUTHZ_FIELD_COUNT | ERROR | The source's ID-clause count doesn't match the SU21 field count. SLIN would flag this as Priority 2 post-deploy; this catches it offline. |
AUTHZ_FIELD_NAME | ERROR | ID-clause names don't match SU21 (missing fields, extra fields, or wrong names). |
Clean up:
cmd /c del {WORK_TEMP}\sap_checkabap_signatures.ps1
Read the result TSV file. The file has a header section (STATUS, ABAP_FILE, NAMING_RULES, TIMESTAMP, TOTAL_DECLARATIONS, TOTAL_SQL_STATEMENTS, TOTAL_ISSUES) followed by a column header row and tab-delimited findings.
| Check Type | Count | Severity |
|---|---|---|
| NAMING | N | WARNING |
| TYPE_NOT_FOUND | N | ERROR |
| TYPE_RESOLVED | N | INFO |
| UNUSED | N | WARNING |
| SQL_TABLE_NOT_FOUND | N | ERROR |
| SQL_FIELD_NOT_FOUND | N | ERROR |
| Code | Severity | Meaning |
|---|---|---|
NAMING | WARNING | Variable name doesn't follow naming convention — wrong prefix |
TYPE_NOT_FOUND | ERROR | Data type not found in local TYPES or SAP Dictionary |
TYPE_RESOLVED | INFO | Data type successfully resolved (structure or data element) |
UNUSED | WARNING | Variable declared but never referenced in the source |
SQL_TABLE_NOT_FOUND | ERROR | SQL table not found in SAP Dictionary |
SQL_FIELD_NOT_FOUND | ERROR | SQL field not found in the referenced table |
SELECT_IN_LOOP | ERROR | SELECT inside LOOP AT itab — pre-select instead (quality rule §12) |
FOR_ALL_ENTRIES_NO_GUARD | ERROR | FOR ALL ENTRIES without IF lt_keys IS NOT INITIAL — empty driver reads ALL rows (§12) |
MESSAGE_E_IN_METHOD | ERROR | MESSAGE e/a/x inside CLASS method — causes UNCAUGHT_EXCEPTION short dump (§11) |
MISSING_AT_HOST_VAR | WARNING | Open-SQL host variable used without @ prefix on release ≥ 7.50 (§13) |
STRING_CONCAT_SQL | ERROR | Dynamic SQL via string concatenation without whitelist (§13) |
MISSING_AUTHZ_CHECK | WARNING | Persistence (UPDATE/INSERT/MODIFY/DELETE/BAPI write) without preceding AUTHORITY-CHECK (§14) |
METHOD_TOO_LONG | WARNING | Method exceeds MODE_MAX_METHOD_LINES from brief (default 50) (§18) |
OBJECT_NAMING | WARNING | Top-level object name (program / FORM / global class / method) does not match sap_object_naming_rules.tsv |
STRUCT_FIELD_MISSING | ERROR | (Step 3.5) <var>-<field> references a field not on the variable's structure type, per _struct_signatures.txt. Catches BAPI_MARA-style typos before deploy. |
STRUCT_TYPE_MISSING | ERROR | (Step 3.5) Struct type marked NOT_FOUND in cache — typo or release-removed. |
AUTHZ_OBJECT_MISSING | ERROR | (Step 3.5) AUTHORITY-CHECK OBJECT name NOT_FOUND in SU21 cache. |
AUTHZ_FIELD_COUNT | ERROR | (Step 3.5) AUTHORITY-CHECK ID-clause count ≠ SU21 field count. Pre-empts SLIN P2 "Wrong number of authorization fields". |
AUTHZ_FIELD_NAME | ERROR | (Step 3.5) AUTHORITY-CHECK ID-clause names don't match SU21. Pre-empts SLIN P2 "Authorization field missing". |
For each finding, show: Line, Variable, Detail, Fix Advice.
On STATUS: SUCCESS — all declarations pass all checks. Congratulate the user.
On STATUS: SUCCESS_WITH_ISSUES — show findings grouped by severity (ERROR first, then WARNING). Suggest:
"Run
/sap-fix-abap <file> <result-file>to apply automatic fixes."
On STATUS: ERROR:
| Error message | Cause | Fix |
|---|---|---|
Cannot create SAP.Functions | SAP GUI not installed or OCX not registered | Install SAP GUI; verify wdtfuncs.ocx |
RFC logon failed | Wrong server/credentials | Verify connection parameters |
ABAP file not found | Wrong path | Verify the file path |
Naming rules file not found | Missing sap-dev-core/shared/tables/abap_naming_rules.tsv | Verify sap-dev-core plugin is installed |
cmd /c del {WORK_TEMP}\sap_checkabap_run.ps1
Keep the result TSV so the user can review it or pass it to sap-fix-abap. To remove:
cmd /c del "THE_RESULT_FILE"
Log the run-end record. Best-effort.
On success:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{WORK_TEMP}\sap_check_abap_run.json" -Status SUCCESS -ExitCode 0
On failure:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{WORK_TEMP}\sap_check_abap_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
Suggested <CLASS>: CHECK_ABAP_FAILED, RFC_LOGON_FAILED.
The generated .vbs file contains the SAP password in plain text and is deleted automatically after execution.
Connection parameters are stored in sap-dev-core settings.json. The password field is marked as
sensitive and masked in the Claude Code UI.
CLASS ... IMPLEMENTATION / ENDCLASS blocks are not currently validated for naming or type checks. This is a known gap — code generated by sap-gen-abap that uses local classes should be manually reviewed for type correctness before deployment.DATA(lv_x) = ..., READ TABLE … INTO DATA(ls_row), LOOP AT … INTO DATA(ls_row), SELECT … INTO TABLE @DATA(lt_data) are scanned and added to the declaration table with type unknown. They are NOT subject to TYPE_NOT_FOUND (no type to check) and NOT subject to UNUSED (the inline-declaration line IS the first-use line). Naming-rule checks still apply.TYPE mara-matnr, TYPE rlgrap-filename, etc. are accepted silently — the type validator skips them rather than emitting a false TYPE_NOT_FOUND. A future enhancement may split on - and validate the field via DD03L.ZHKFIXEDVALS), the SQL check will report SQL_TABLE_NOT_FOUND until the table is created in SAP. This is expected behavior — create the table via sap-se11 first, then re-run the check.MESSAGE types E, A, X inside CLASS ... IMPLEMENTATION methods will cause UNCAUGHT_EXCEPTION at runtime. Replace with exception classes or result table pattern. Flag these as potential short dump causes during static analysis.CALL FUNCTION for a BAPI uses a local table typed differently from the BAPI's expected structure, flag as potential CALL_FUNCTION_CONFLICT_LENG. Example: DATA lt TYPE TABLE OF ty_custom passed to materialdescription which expects bapi_makt. Always use the exact BAPI structure type for TABLES and EXPORTING/IMPORTING parameters.has_field_separator with flat char tables: has_field_separator = 'X' with TABLE OF charNNNN (single-field flat table) strips tab delimiters and only reads the first column. Remove has_field_separator when using flat char tables and parse tab separators manually with SPLIT.This skill is part of the ABAP quality pipeline:
MAX_PRIORITYnpx claudepluginhub sapdev-ai/sap-dev --plugin sap-gen-codeSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.