Skip to content

Incorrect DATA to FINAL conversion when row is modified inside LOOP AT GROUP #540

Description

@bnichell

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

  • ABAP cleaner: v1.27.0

Steps to reproduce

  1. Create the report below (note the unformatted DATA(rows) = read_rows( ).).
  2. Run ABAP cleaner on the source — the line is rewritten to FINAL(rows) = read_rows( )..
  3. 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( ).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions