ABAP Cleaner: Limpieza Automática de Código

Kategorie
DevOps
Veröffentlicht
Autor
Johannes

ABAP Cleaner es una herramienta de código abierto que limpia, formatea y moderniza automáticamente el código ABAP. Aplica más de 60 reglas de limpieza y ayuda a mantener estándares de código uniformes en todo el equipo.

¿Qué es ABAP Cleaner?

ABAP Cleaner analiza código ABAP y lo transforma automáticamente según reglas configurables. A diferencia del Pretty Printer, ABAP Cleaner va mucho más allá del simple formateo y también moderniza construcciones del lenguaje.

AspectoPretty PrinterABAP Cleaner
EnfoqueIndentación, mayúsculas/minúsculasModernización completa del código
Reglas~5 reglas básicas60+ reglas configurables
ConstruccionesSin cambiosModernización (p.ej. NEW en lugar de CREATE OBJECT)
Líneas vacíasReglas simplesAgrupación inteligente
ComentariosSin cambiosAlineación y formateo
Rendimiento-Eliminación de sentencias innecesarias
PerfilesPredefinidosConfigurables individualmente

Ventajas

  • Consistencia: Estilo de código uniforme en todo el equipo
  • Productividad: Limpieza automática ahorra trabajo manual
  • Modernización: Sintaxis antigua se actualiza automáticamente
  • Legibilidad: Mejor estructuración y formateo
  • Code Review: Menos discusiones sobre cuestiones de estilo
  • Onboarding: Nuevos miembros del equipo escriben código conforme desde el inicio

Instalación en ADT

Requisitos previos

  • Eclipse con ADT (ABAP Development Tools)
  • Eclipse versión 2022-03 o posterior
  • Java 11 o posterior

Instalación vía Eclipse Marketplace

  1. Abrir Eclipse y navegar a Help → Eclipse Marketplace
  2. Buscar “ABAP Cleaner”
  3. Clic en Install y seguir las instrucciones
  4. Reiniciar Eclipse

Instalación manual vía Update Site

Si el Marketplace no está disponible:

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

Verificar instalación

Después del reinicio, ABAP Cleaner debería estar disponible:

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

Reglas de limpieza más importantes

ABAP Cleaner agrupa sus reglas en categorías. Estas son las más importantes:

1. Empty Lines - Optimizar líneas vacías

Agrupación inteligente de bloques de código:

Antes:

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.

Después:

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 - Modernizar declaraciones

Antes:

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.

Después:

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 - Usar sintaxis ABAP moderna

Antes:

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.

Después:

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 - Eliminar comandos innecesarios

Antes:

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

Después:

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

5. Alignment - Mejorar alineación

Antes:

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.

Después:

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 - Formateo

Antes:

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

Después:

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 - Usar VALUE y NEW

Antes:

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.

Después:

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

8. Comments - Formatear comentarios

Antes:

"Este método calcula el precio total
"incl. IVA y descuento.
"Parámetro: iv_net_price - Precio neto
" iv_discount - Descuento en porcentaje
"Retorno: Precio bruto
METHOD calculate_gross_price.

Después:

" Este método calcula el precio total
" incl. IVA y descuento.
" Parámetro: iv_net_price - Precio neto
" iv_discount - Descuento en porcentaje
" Retorno: Precio bruto
METHOD calculate_gross_price.

Configuración y perfiles

Concepto de perfiles

ABAP Cleaner usa perfiles para la configuración. Un perfil define qué reglas están activas y cómo se aplican.

┌──────────────────────────────────────────────────────────────┐
│ Profile Manager │
├──────────────────────────────────────────────────────────────┤
│ │
│ Perfiles: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ [★] Default │ │
│ │ [ ] Team Standard │ │
│ │ [ ] Strict Modernization │ │
│ │ [ ] Minimal Changes │ │
│ │ [ ] + Crear nuevo perfil... │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ [ Editar ] [ Copiar ] [ Eliminar ] [ Exportar ] [ Importar ] │
│ │
└──────────────────────────────────────────────────────────────┘

Crear y configurar perfil

  1. Window → Preferences → ABAP Cleaner abrir
  2. Profiles seleccionar
  3. Create new profile clic

Configuración de reglas

Cada regla puede configurarse individualmente:

┌──────────────────────────────────────────────────────────────┐
│ Rule Configuration: "Use inline declarations" │
├──────────────────────────────────────────────────────────────┤
│ │
│ ☑ Regla habilitada │
│ │
│ Opciones: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Convertir declaraciones DATA: │ │
│ │ ○ Solo si se usa una vez │ │
│ │ ● Si se usa en el mismo bloque │ │
│ │ ○ Siempre (agresivo) │ │
│ │ │ │
│ │ ☑ Convertir FIELD-SYMBOLS │ │
│ │ ☑ Mantener declaraciones para tipos complejos │ │
│ │ ☐ Convertir incluso si se pierde el tipo │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Vista previa: │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ DATA lv_name TYPE │ │ DATA(lv_name) = │ │
│ │ string. │→│ get_name( ). │ │
│ │ lv_name = get_name( │ │ │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘

Exportar y compartir perfil

Los perfiles pueden exportarse como archivo JSON y compartirse en el equipo:

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

Distribuir en el equipo:

1. Exportar perfil: Preferences → ABAP Cleaner → Export Profile
2. Colocar archivo JSON en repositorio Git: /config/abap-cleaner-profile.json
3. Miembros del equipo importan: Preferences → ABAP Cleaner → Import Profile

Uso diario

Modo interactivo

El modo interactivo muestra todos los cambios antes de aplicarlos:

Atajo: Ctrl+4 (o Source → Clean Up With Interactive ABAP Cleaner...)
┌──────────────────────────────────────────────────────────────┐
│ Interactive ABAP Cleaner │
├──────────────────────────────────────────────────────────────┤
│ │
│ Regla: "Replace obsolete ADD with modern syntax" │
│ │
│ ┌─────────────────────────────┬─────────────────────────┐ │
│ │ Antes │ Después │ │
│ ├─────────────────────────────┼─────────────────────────┤ │
│ │ ADD 1 TO lv_count. │ lv_count += 1. │ │
│ │ ADD lv_amount TO lv_total. │ lv_total += lv_amount. │ │
│ └─────────────────────────────┴─────────────────────────┘ │
│ │
│ [✓ Aplicar] [✗ Omitir] [Aplicar todo] [Omitir todo] [Info] │
│ │
│ Cambios: 2 de 15 aplicados | Perfil: Team Standard │
│ │
└──────────────────────────────────────────────────────────────┘

Modo automático

Aplicar todas las reglas sin consulta:

Atajo: Ctrl+Shift+4 (o Source → Clean Up With Automatic ABAP Cleaner)

Limpieza al guardar

ABAP Cleaner puede ejecutarse automáticamente al guardar:

Preferences → ABAP Cleaner → Clean-up on save
☑ Habilitar limpieza al guardar
Perfil: [Team Standard ▼]
Alcance:
○ Solo método actual
● Bloques de código modificados
○ Código fuente completo
☑ Mostrar notificación después de limpieza

Configurar atajos de teclado

Window → Preferences → General → Keys
Buscar: "ABAP Cleaner"
Resultados:
- 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

Ejemplo completo antes/después

Antes: Código Legacy

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.

Después: Código modernizado

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.

Resumen de cambios

CategoríaCambio
Líneas vacíasLíneas vacías innecesarias eliminadas, agrupación lógica
DeclaracionesDeclaraciones DATA: encadenadas separadas
CREATE OBJECTReemplazado por NEW #( )
CLEARCLEAR innecesario eliminado (variable se asigna directamente)
CALL METHODEscritura funcional utilizada
EQ/NEReemplazado por =/<>
Inline DATADeclaraciones inline donde tiene sentido
REDUCELOOP AT con ADD reemplazado por REDUCE
AlignmentAlineación consistente de parámetros

Integración en CI/CD

ABAP Cleaner puede usarse como parte del pipeline CI/CD para verificar estándares de código:

.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: |
# Descargar ABAP Cleaner CLI
wget https://github.com/SAP/abap-cleaner/releases/latest/download/abap-cleaner-cli.jar
# Verificar si serían necesarios cambios de limpieza
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

Mejores prácticas

TemaRecomendación
Selección de perfilComenzar con perfil Default, ajustar gradualmente
Estándar del equipoDefinir un perfil común para todo el equipo
Cleanup on SaveActivar para código consistente
Modo interactivoEn código legacy, primero verificar interactivamente
Code ReviewEjecutar cleanup antes del review
Nuevas reglasRevisar nuevas reglas con actualizaciones de ABAP Cleaner
ExcepcionesReglas desactivables para archivos específicos
CI/CDIntegrar como verificación de estilo en pipeline
MigraciónLimpiar código legacy gradualmente, no todo a la vez
DocumentaciónDocumentar decisiones del perfil del equipo

Temas relacionados