ABAP Cleaner: Automatisches Code-Cleanup

kategorie
DevOps
Veröffentlicht
autor
Johannes

ABAP Cleaner ist ein Open-Source-Tool, das ABAP-Code automatisch aufräumt, formatiert und modernisiert. Es wendet über 60 Cleanup-Regeln an und hilft dabei, Code-Standards teamweit einheitlich durchzusetzen.

Was ist ABAP Cleaner?

ABAP Cleaner analysiert ABAP-Code und transformiert ihn automatisch nach konfigurierbaren Regeln. Im Gegensatz zum Pretty Printer geht ABAP Cleaner weit über reine Formatierung hinaus und modernisiert auch Sprachkonstrukte.

AspektPretty PrinterABAP Cleaner
FokusEinrückung, Groß-/KleinschreibungVollständige Code-Modernisierung
Regeln~5 Grundregeln60+ konfigurierbare Regeln
SprachkonstrukteKeine ÄnderungModernisierung (z.B. NEW statt CREATE OBJECT)
LeerzeilenEinfache RegelnIntelligente Gruppierung
KommentareKeine ÄnderungAusrichtung und Formatierung
Performance-Entfernung unnötiger Statements
ProfileFest vorgegebenIndividuell konfigurierbar

Vorteile

  • Konsistenz: Einheitlicher Code-Stil im gesamten Team
  • Produktivität: Automatisches Cleanup spart manuelle Arbeit
  • Modernisierung: Alte Syntax wird automatisch aktualisiert
  • Lesbarkeit: Bessere Strukturierung und Formatierung
  • Code Review: Weniger Diskussionen über Stil-Fragen
  • Onboarding: Neue Teammitglieder schreiben sofort konformen Code

Installation in ADT

Voraussetzungen

  • Eclipse mit ADT (ABAP Development Tools)
  • Eclipse Version 2022-03 oder neuer
  • Java 11 oder neuer

Installation via Eclipse Marketplace

  1. Eclipse öffnen und zu Help → Eclipse Marketplace navigieren
  2. Suche nach “ABAP Cleaner”
  3. Install klicken und den Anweisungen folgen
  4. Eclipse neu starten

Manuelle Installation via Update Site

Falls der Marketplace nicht verfügbar ist:

Help → Install New Software...
Update Site URL:
https://sap.github.io/abap-cleaner/updatesite
Verfügbare Features:
☑ ABAP Cleaner
→ Next → Accept License → Finish → Restart Eclipse

Installation verifizieren

Nach dem Neustart sollte ABAP Cleaner verfügbar sein:

┌──────────────────────────────────────────────────────────────┐
│ ADT Toolbar │
├──────────────────────────────────────────────────────────────┤
│ │
│ [📁] [💾] [↩️] [↪️] ... [🧹 ABAP Cleaner] │
│ │
│ Oder: Source → Clean Up With Interactive ABAP Cleaner... │
│ Shortcut: Ctrl+4 (konfigurierbar) │
│ │
└──────────────────────────────────────────────────────────────┘

Wichtigste Cleanup-Regeln

ABAP Cleaner gruppiert seine Regeln in Kategorien. Hier sind die wichtigsten:

1. Empty Lines - Leerzeilen optimieren

Intelligente Gruppierung von Code-Blöcken:

Vorher:

METHOD process_order.
DATA lv_status TYPE string.
DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status
FROM zorders
INTO lv_status
WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'.
lv_amount = calculate_total( ).
update_order( lv_amount ).
ENDIF.
ENDMETHOD.

Nachher:

METHOD process_order.
DATA lv_status TYPE string.
DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status
FROM zorders
INTO lv_status
WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'.
lv_amount = calculate_total( ).
update_order( lv_amount ).
ENDIF.
ENDMETHOD.

2. Declarations - Deklarationen modernisieren

Vorher:

DATA: lv_count TYPE i,
lt_items TYPE TABLE OF zitem,
ls_item TYPE zitem,
lo_processor TYPE REF TO zcl_processor.
CREATE OBJECT lo_processor.
CLEAR lt_items.

Nachher:

DATA lv_count TYPE i.
DATA lt_items TYPE TABLE OF zitem.
DATA ls_item TYPE zitem.
DATA lo_processor TYPE REF TO zcl_processor.
lo_processor = NEW #( ).
CLEAR lt_items.

3. Syntax - Moderne ABAP-Syntax verwenden

Vorher:

CALL METHOD lo_service->get_data
EXPORTING
iv_id = lv_id
RECEIVING
rt_data = lt_data.
IF lt_data IS INITIAL.
" ...
ENDIF.
LOOP AT lt_data INTO ls_data.
MOVE-CORRESPONDING ls_data TO ls_result.
APPEND ls_result TO lt_result.
ENDLOOP.

Nachher:

lt_data = lo_service->get_data( iv_id = lv_id ).
IF lt_data IS INITIAL.
" ...
ENDIF.
LOOP AT lt_data INTO DATA(ls_data).
lt_result = VALUE #( BASE lt_result ( CORRESPONDING #( ls_data ) ) ).
ENDLOOP.

4. Commands - Überflüssige Befehle entfernen

Vorher:

CLEAR lv_result.
lv_result = calculate_value( ).
IF sy-subrc = 0.
" Erfolg
ENDIF.
MOVE lv_source TO lv_target.

Nachher:

lv_result = calculate_value( ).
IF sy-subrc = 0.
" Erfolg
ENDIF.
lv_target = lv_source.

5. Alignment - Ausrichtung verbessern

Vorher:

ls_order-order_id = lv_id.
ls_order-customer = lv_customer.
ls_order-status = 'NEW'.
ls_order-created_at = sy-datum.
ls_order-created_by = sy-uname.
ls_order-amount = lv_amount.

Nachher:

ls_order-order_id = lv_id.
ls_order-customer = lv_customer.
ls_order-status = 'NEW'.
ls_order-created_at = sy-datum.
ls_order-created_by = sy-uname.
ls_order-amount = lv_amount.

6. Pretty Print - Formatierung

Vorher:

IF LV_STATUS='OPEN'AND LV_AMOUNT>100.
CALL METHOD LO_SERVICE->PROCESS(EXPORTING IV_ID=LV_ID CHANGING CT_DATA=LT_DATA).
ENDIF.

Nachher:

IF lv_status = 'OPEN' AND lv_amount > 100.
lo_service->process( EXPORTING iv_id = lv_id
CHANGING ct_data = lt_data ).
ENDIF.

7. Inline Declarations - VALUE und NEW nutzen

Vorher:

DATA lt_orders TYPE TABLE OF zorder.
DATA ls_order TYPE zorder.
DATA lo_service TYPE REF TO zcl_order_service.
CREATE OBJECT lo_service
EXPORTING
iv_client = sy-mandt.
ls_order-order_id = '12345'.
ls_order-status = 'NEW'.
APPEND ls_order TO lt_orders.

Nachher:

DATA(lo_service) = NEW zcl_order_service( iv_client = sy-mandt ).
DATA(lt_orders) = VALUE zorders_t(
( order_id = '12345' status = 'NEW' )
).

8. Comments - Kommentare formatieren

Vorher:

"Diese Methode berechnet den Gesamtpreis
"inkl. MwSt und Rabatt.
"Parameter: iv_net_price - Nettopreis
" iv_discount - Rabatt in Prozent
"Rückgabe: Bruttopreis
METHOD calculate_gross_price.

Nachher:

" Diese Methode berechnet den Gesamtpreis
" inkl. MwSt und Rabatt.
" Parameter: iv_net_price - Nettopreis
" iv_discount - Rabatt in Prozent
" Rückgabe: Bruttopreis
METHOD calculate_gross_price.

Konfiguration und Profile

Profile-Konzept

ABAP Cleaner verwendet Profile zur Konfiguration. Ein Profil definiert, welche Regeln aktiv sind und wie sie angewendet werden.

┌──────────────────────────────────────────────────────────────┐
│ Profile Manager │
├──────────────────────────────────────────────────────────────┤
│ │
│ Profile: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ [★] Default │ │
│ │ [ ] Team Standard │ │
│ │ [ ] Strict Modernization │ │
│ │ [ ] Minimal Changes │ │
│ │ [ ] + Create new profile... │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ [ Edit ] [ Copy ] [ Delete ] [ Export ] [ Import ] │
│ │
└──────────────────────────────────────────────────────────────┘

Profil erstellen und konfigurieren

  1. Window → Preferences → ABAP Cleaner öffnen
  2. Profiles auswählen
  3. Create new profile klicken

Regelkonfiguration

Jede Regel kann individuell eingestellt werden:

┌──────────────────────────────────────────────────────────────┐
│ Rule Configuration: "Use inline declarations" │
├──────────────────────────────────────────────────────────────┤
│ │
│ ☑ Rule enabled │
│ │
│ Options: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Convert DATA declarations: │ │
│ │ ○ Only if used once │ │
│ │ ● If used in same block │ │
│ │ ○ Always (aggressive) │ │
│ │ │ │
│ │ ☑ Convert FIELD-SYMBOLS │ │
│ │ ☑ Keep declarations for complex types │ │
│ │ ☐ Convert even if type is lost │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Preview: │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ DATA lv_name TYPE │ │ DATA(lv_name) = │ │
│ │ string. │→│ get_name( ). │ │
│ │ lv_name = get_name( │ │ │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘

Profil exportieren und teilen

Profile können als JSON-Datei exportiert und im Team geteilt werden:

{
"profileName": "Team Standard",
"rules": {
"EmptyLinesInClassDefinition": {
"enabled": true,
"maxEmptyLines": 1
},
"UseInlineDeclaration": {
"enabled": true,
"convertFieldSymbols": true,
"onlyIfUsedOnce": false
},
"ReplaceCreateObject": {
"enabled": true
},
"RemoveUnusedVariables": {
"enabled": false
}
}
}

Im Team verteilen:

1. Profil exportieren: Preferences → ABAP Cleaner → Export Profile
2. JSON-Datei ins Git-Repository legen: /config/abap-cleaner-profile.json
3. Teammitglieder importieren: Preferences → ABAP Cleaner → Import Profile

Verwendung im Alltag

Interaktiver Modus

Der interaktive Modus zeigt alle Änderungen vor der Anwendung:

Shortcut: Ctrl+4 (oder Source → Clean Up With Interactive ABAP Cleaner...)
┌──────────────────────────────────────────────────────────────┐
│ Interactive ABAP Cleaner │
├──────────────────────────────────────────────────────────────┤
│ │
│ Rule: "Replace obsolete ADD with modern syntax" │
│ │
│ ┌─────────────────────────────┬─────────────────────────┐ │
│ │ Before │ After │ │
│ ├─────────────────────────────┼─────────────────────────┤ │
│ │ ADD 1 TO lv_count. │ lv_count += 1. │ │
│ │ ADD lv_amount TO lv_total. │ lv_total += lv_amount. │ │
│ └─────────────────────────────┴─────────────────────────┘ │
│ │
│ [✓ Apply] [✗ Skip] [Apply All] [Skip All] [Show Rule Info] │
│ │
│ Changes: 2 of 15 applied | Profile: Team Standard │
│ │
└──────────────────────────────────────────────────────────────┘

Automatischer Modus

Alle Regeln ohne Rückfrage anwenden:

Shortcut: Ctrl+Shift+4 (oder Source → Clean Up With Automatic ABAP Cleaner)

Cleanup bei Save

ABAP Cleaner kann bei jedem Speichern automatisch ausgeführt werden:

Preferences → ABAP Cleaner → Clean-up on save
☑ Enable clean-up on save
Profile: [Team Standard ▼]
Scope:
○ Current method only
● Changed code blocks
○ Entire source code
☑ Show notification after clean-up

Tastenkürzel konfigurieren

Window → Preferences → General → Keys
Suche: "ABAP Cleaner"
Ergebnisse:
- Clean Up With Interactive ABAP Cleaner: Ctrl+4
- Clean Up With Automatic ABAP Cleaner: Ctrl+Shift+4
- Open ABAP Cleaner Preferences: Ctrl+Shift+Alt+4

Vollständiges Vorher/Nachher-Beispiel

Vorher: Legacy-Code

CLASS zcl_order_processor DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS: process_order
IMPORTING
iv_order_id TYPE zorder_id
RETURNING
VALUE(rv_success) TYPE abap_bool
RAISING
zcx_order_error.
PRIVATE SECTION.
DATA: mv_client TYPE mandt.
DATA: mo_logger TYPE REF TO zcl_logger.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD process_order.
DATA: lv_status TYPE zorder_status,
lt_items TYPE TABLE OF zorder_item,
ls_item TYPE zorder_item,
lv_total TYPE p DECIMALS 2,
lo_validator TYPE REF TO zcl_order_validator.
CREATE OBJECT lo_validator.
CLEAR lv_total.
SELECT SINGLE status
FROM zorders
INTO lv_status
WHERE order_id EQ iv_order_id.
IF sy-subrc NE 0.
RAISE EXCEPTION TYPE zcx_order_error.
ENDIF.
IF lv_status EQ 'CLOSED'.
rv_success = abap_false.
RETURN.
ENDIF.
SELECT *
FROM zorder_items
INTO TABLE lt_items
WHERE order_id EQ iv_order_id.
LOOP AT lt_items INTO ls_item.
ADD ls_item-amount TO lv_total.
ENDLOOP.
CALL METHOD lo_validator->validate
EXPORTING
iv_order_id = iv_order_id
iv_total = lv_total
RECEIVING
rv_valid = DATA(lv_valid).
IF lv_valid EQ abap_true.
UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id.
rv_success = abap_true.
ELSE.
rv_success = abap_false.
ENDIF.
ENDMETHOD.
ENDCLASS.

Nachher: Modernisierter Code

CLASS zcl_order_processor DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS process_order
IMPORTING iv_order_id TYPE zorder_id
RETURNING VALUE(rv_success) TYPE abap_bool
RAISING zcx_order_error.
PRIVATE SECTION.
DATA mv_client TYPE mandt.
DATA mo_logger TYPE REF TO zcl_logger.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD process_order.
DATA(lo_validator) = NEW zcl_order_validator( ).
SELECT SINGLE status
FROM zorders
INTO @DATA(lv_status)
WHERE order_id = @iv_order_id.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_order_error.
ENDIF.
IF lv_status = 'CLOSED'.
rv_success = abap_false.
RETURN.
ENDIF.
SELECT *
FROM zorder_items
INTO TABLE @DATA(lt_items)
WHERE order_id = @iv_order_id.
DATA(lv_total) = REDUCE #( INIT sum = 0
FOR ls_item IN lt_items
NEXT sum += ls_item-amount ).
DATA(lv_valid) = lo_validator->validate( iv_order_id = iv_order_id
iv_total = lv_total ).
IF lv_valid = abap_true.
UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id.
rv_success = abap_true.
ELSE.
rv_success = abap_false.
ENDIF.
ENDMETHOD.
ENDCLASS.

Zusammenfassung der Änderungen

KategorieÄnderung
LeerzeilenÜberflüssige Leerzeilen entfernt, logische Gruppierung
DeklarationenKettete DATA:-Deklarationen aufgelöst
CREATE OBJECTErsetzt durch NEW #( )
CLEARUnnötiges CLEAR entfernt (Variable wird direkt zugewiesen)
CALL METHODFunktionale Schreibweise verwendet
EQ/NEErsetzt durch =/<>
Inline DATAInline-Deklarationen wo sinnvoll
REDUCELOOP AT mit ADD ersetzt durch REDUCE
AlignmentKonsistente Ausrichtung der Parameter

Integration in CI/CD

ABAP Cleaner kann als Teil der CI/CD-Pipeline genutzt werden, um Code-Standards zu prüfen:

.github/workflows/abap-style.yml
name: ABAP Style Check
on:
pull_request:
paths:
- 'src/**.abap'
jobs:
style-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Run ABAP Cleaner Check
run: |
# ABAP Cleaner CLI herunterladen
wget https://github.com/SAP/abap-cleaner/releases/latest/download/abap-cleaner-cli.jar
# Prüfen, ob Cleanup-Änderungen nötig wären
java -jar abap-cleaner-cli.jar \
--profile config/abap-cleaner-profile.json \
--check-only \
--source src/
- name: Upload Report
if: failure()
uses: actions/upload-artifact@v4
with:
name: cleanup-report
path: cleanup-report.txt

Best Practices

ThemaEmpfehlung
Profil-AuswahlMit Default-Profil starten, schrittweise anpassen
Team-StandardEin gemeinsames Profil für das gesamte Team definieren
Cleanup on SaveAktivieren für konsistenten Code
Interaktiver ModusBei Legacy-Code erst interaktiv prüfen
Code ReviewCleanup vor dem Review durchführen
Neue RegelnBei ABAP Cleaner-Updates neue Regeln prüfen
AusnahmenRegeln für spezifische Dateien deaktivierbar
CI/CDAls Style-Check in Pipeline integrieren
MigrationLegacy-Code schrittweise bereinigen, nicht alles auf einmal
DokumentationTeam-Profil-Entscheidungen dokumentieren

Weiterführende Themen