ABAP cleaner promotes DATA(...) to FINAL(...) when the variable is written through LOOP AT GROUP, causing runtime error
Summary
ABAP cleaner incorrectly converts DATA(var) = ... to FINAL(var) = ... when the table is later modified via a LOOP AT ... GROUP BY ... ASSIGNING <group> followed by an inner LOOP AT GROUP <group> ASSIGNING <row> that writes to a component of <row>.
Because the write to the source table happens through two levels of indirection (group reference → LOOP AT GROUP → row field-symbol), the cleaner's data-flow analysis does not see it as a write and treats the variable as eligible for FINAL. After formatting, executing the code dumps because the anonymous data object behind FINAL(...) is immutable.
Environment
Steps to reproduce
- Create the report below (note the unformatted
DATA(rows) = read_rows( ).).
- Run ABAP cleaner on the source — the line is rewritten to
FINAL(rows) = read_rows( )..
- Activate and execute. A runtime error is raised at
<row>-value = <row>-value * 10..
Minimal reproducible example
*&---------------------------------------------------------------------*
*& Report ZR_CLEANER_FINAL_GROUP_BUG
*&---------------------------------------------------------------------*
REPORT zr_cleaner_final_group_bug.
CLASS lcl_demo DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_row,
group_key TYPE i,
value TYPE i,
END OF ty_row,
tt_rows TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY.
METHODS run.
PRIVATE SECTION.
METHODS read_rows RETURNING VALUE(result) TYPE tt_rows.
ENDCLASS.
CLASS lcl_demo IMPLEMENTATION.
METHOD read_rows.
result = VALUE #( ( group_key = 1 value = 10 )
( group_key = 1 value = 20 )
( group_key = 2 value = 30 ) ).
ENDMETHOD.
METHOD run.
" Before ABAP cleaner: DATA(rows) = read_rows( ).
" After ABAP cleaner: FINAL(rows) = read_rows( ).
DATA(rows) = read_rows( ).
LOOP AT rows INTO DATA(key_for_grouping)
GROUP BY ( group_key = key_for_grouping-group_key )
ASSIGNING FIELD-SYMBOL(<group>).
LOOP AT GROUP <group> ASSIGNING FIELD-SYMBOL(<row>).
" <row> points into `rows`. If `rows` is FINAL, this write dumps.
<row>-value = <row>-value * 10.
ENDLOOP.
ENDLOOP.
LOOP AT rows ASSIGNING FIELD-SYMBOL(<out>).
WRITE: / <out>-group_key, <out>-value.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
NEW lcl_demo( )->run( ).
ABAP cleaner promotes
DATA(...)toFINAL(...)when the variable is written throughLOOP AT GROUP, causing runtime errorSummary
ABAP cleaner incorrectly converts
DATA(var) = ...toFINAL(var) = ...when the table is later modified via aLOOP AT ... GROUP BY ... ASSIGNING <group>followed by an innerLOOP AT GROUP <group> ASSIGNING <row>that writes to a component of<row>.Because the write to the source table happens through two levels of indirection (group reference →
LOOP AT GROUP→ row field-symbol), the cleaner's data-flow analysis does not see it as a write and treats the variable as eligible forFINAL. After formatting, executing the code dumps because the anonymous data object behindFINAL(...)is immutable.Environment
Steps to reproduce
DATA(rows) = read_rows( ).).FINAL(rows) = read_rows( )..<row>-value = <row>-value * 10..Minimal reproducible example