Skip to content

Commit 612b9bc

Browse files
lucasborinEugen Günther
andauthored
Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT (#355)
* implements #286 * Update changelog.txt * Update check_documentation.md * demo + description * Create prefer-line-exists.md * Update y_check_unit_test_assert.clas.abap * fixing from/to * Update y_check_prefer_line_exists.clas.testclasses.abap Co-authored-by: Eugen Günther <[email protected]>
1 parent 9a3cbc3 commit 612b9bc

File tree

7 files changed

+294
-0
lines changed

7 files changed

+294
-0
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Whenever you upgrade code pal for ABAP, it is highly recommended to execute the
1414

1515
2021-04-** v.1.14.0
1616
------------------
17+
+ Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT (#355)
1718
+ Additional option to disable exceptions/pragmas (#329)
1819
! abapLint Rules
1920
* Diffs for TABL (#359)

docs/check_documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- [Number of Output Parameter](checks/number-output-parameter.md)
4141
- [Prefer CASE to ELSEIF](checks/prefer-case-to-elseif.md)
4242
- [Prefer IS NOT to NOT IS](checks/prefer-is-not-to-not-is.md)
43+
- [Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT](checks/prefer-line-exists.md)
4344
- [Prefer NEW to CREATE OBJECT](checks/prefer-new-to-create-object.md)
4445
- [Pseudo Comment Usage](checks/pseudo-comment-usage.md)
4546
- [Omit Optional EXPORTING](checks/omit-optional-exporting.md)

docs/checks/prefer-line-exists.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[code pal for ABAP](../../README.md) > [Documentation](../check_documentation.md) > [Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT](prefer-line-exists.md)
2+
3+
## Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT
4+
5+
### What is the Intent of the Check?
6+
7+
Prefer `LINE_EXISTS` or `LINE_INDEX` over `READ TABLE` or `LOOP AT` as they avoid needlessly longer statements.
8+
9+
### How to solve the issue?
10+
11+
Preferably, use `LINE_EXISTS` to check whether the row of an internal table exists, and `LINE_INDEX` to check the row index.
12+
13+
### What to do in case of exception?
14+
15+
In exceptional cases, you can suppress this finding by using the pseudo comment `"#EC PREF_LINE_EX`:
16+
17+
```abap
18+
READ TABLE my_table TRANSPORTING NO FIELDS WITH KEY key = 'A'. "#EC PREF_LINE_EX
19+
```
20+
21+
```abap
22+
LOOP AT my_table REFERENCE INTO DATA(line) WHERE key = 'A'. "#EC PREF_LINE_EX
23+
...
24+
ENDLOOP.
25+
```
26+
27+
### Example
28+
29+
Before the check:
30+
31+
```abap
32+
READ TABLE my_table TRANSPORTING NO FIELDS WITH KEY key = 'A'.
33+
34+
IF sy-subrc = 0.
35+
line_index = sy-tabix.
36+
line_exists = abap_true.
37+
ENDIF.
38+
```
39+
40+
```abap
41+
LOOP AT my_table REFERENCE INTO DATA(line) WHERE key = 'A'.
42+
line_index = sy-tabix.
43+
line_exists = abap_true.
44+
EXIT.
45+
ENDLOOP.
46+
```
47+
48+
After the check:
49+
50+
```abap
51+
DATA(index) = line_index( my_table[ key = 'A' ] ).
52+
DATA(exists) = xsdbool( line_exists( my_table[ key = 'A' ] ) ).
53+
```
54+
55+
### Further Readings & Knowledge
56+
57+
* [ABAP Styleguides on Clean Code](https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-line_exists-to-read-table-or-loop-at)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
CLASS y_check_prefer_line_exists DEFINITION PUBLIC INHERITING FROM y_check_base CREATE PUBLIC.
2+
PUBLIC SECTION.
3+
METHODS constructor.
4+
5+
PROTECTED SECTION.
6+
METHODS inspect_tokens REDEFINITION.
7+
8+
METHODS get_statement_inline IMPORTING statement TYPE sstmnt
9+
RETURNING VALUE(result) TYPE string.
10+
11+
ENDCLASS.
12+
13+
14+
15+
CLASS y_check_prefer_line_exists IMPLEMENTATION.
16+
17+
18+
METHOD constructor.
19+
super->constructor( ).
20+
21+
settings-pseudo_comment = '"#EC PREF_LINE_EX' ##NO_TEXT.
22+
settings-disable_threshold_selection = abap_true.
23+
settings-threshold = 0.
24+
settings-prio = c_warning.
25+
settings-documentation = |{ c_docs_path-checks }prefer-line-exists.md|.
26+
27+
set_check_message( 'Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT!' ).
28+
ENDMETHOD.
29+
30+
31+
METHOD inspect_tokens.
32+
CHECK get_token_abs( statement-from ) = 'READ'
33+
OR get_token_abs( statement-from ) = 'LOOP'.
34+
35+
DATA(inline) = get_statement_inline( statement ).
36+
37+
IF inline NP '* TRANSPORTING NO FIELDS *'.
38+
RETURN.
39+
ENDIF.
40+
41+
IF inline CP '* FROM *'
42+
OR inline CP '* TO *'.
43+
RETURN.
44+
ENDIF.
45+
46+
DATA(configuration) = detect_check_configuration( statement ).
47+
48+
IF configuration IS INITIAL.
49+
RETURN.
50+
ENDIF.
51+
52+
raise_error( statement_level = statement-level
53+
statement_index = index
54+
statement_from = statement-from
55+
error_priority = configuration-prio ).
56+
ENDMETHOD.
57+
58+
59+
METHOD get_statement_inline.
60+
LOOP AT ref_scan_manager->tokens ASSIGNING FIELD-SYMBOL(<token>)
61+
FROM statement-from TO statement-to.
62+
result = |{ result } { <token>-str }|.
63+
ENDLOOP.
64+
ENDMETHOD.
65+
66+
ENDCLASS.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
*"* use this source file for your ABAP unit test classes
2+
CLASS ltc_read_table DEFINITION INHERITING FROM y_unit_test_base FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
3+
PROTECTED SECTION.
4+
METHODS get_cut REDEFINITION.
5+
METHODS get_code_with_issue REDEFINITION.
6+
METHODS get_code_without_issue REDEFINITION.
7+
METHODS get_code_with_exemption REDEFINITION.
8+
ENDCLASS.
9+
10+
CLASS ltc_read_table IMPLEMENTATION.
11+
12+
METHOD get_cut.
13+
result ?= NEW y_check_prefer_line_exists( ).
14+
ENDMETHOD.
15+
16+
METHOD get_code_with_issue.
17+
result = VALUE #(
18+
( 'REPORT y_example. ' )
19+
20+
( ' START-OF-SELECTION. ' )
21+
( ' DATA tadir TYPE TABLE OF tadir. ' )
22+
( ' DATA exists TYPE abap_bool. ' )
23+
24+
( | READ TABLE tadir TRANSPORTING NO FIELDS WITH KEY object = 'y_check_prefer_line_exists'. | )
25+
26+
( ' IF sy-subrc = 0. ' )
27+
( ' exists = abap_true. ' )
28+
( ' ENDIF. ' )
29+
).
30+
ENDMETHOD.
31+
32+
METHOD get_code_without_issue.
33+
result = VALUE #(
34+
( 'REPORT y_example. ' )
35+
36+
( ' START-OF-SELECTION. ' )
37+
( ' DATA tadir TYPE TABLE OF tadir. ' )
38+
( | DATA(exists) = xsdbool( line_exists( tadir[ object = 'y_check_prefer_line_exists' ] ) ). | )
39+
( | DATA(index) = line_index( tadir[ object = 'y_check_prefer_line_exists' ] ). | )
40+
).
41+
ENDMETHOD.
42+
43+
METHOD get_code_with_exemption.
44+
result = VALUE #(
45+
( 'REPORT y_example. ' )
46+
47+
( ' START-OF-SELECTION. ' )
48+
( ' DATA tadir TYPE TABLE OF tadir. ' )
49+
( ' DATA exists TYPE abap_bool. ' )
50+
51+
( | READ TABLE tadir TRANSPORTING NO FIELDS WITH KEY object = 'y_check_prefer_line_exists'. "#EC PREF_LINE_EX | )
52+
53+
( ' IF sy-subrc = 0. ' )
54+
( ' exists = abap_true. ' )
55+
( ' ENDIF. ' )
56+
).
57+
ENDMETHOD.
58+
59+
ENDCLASS.
60+
61+
62+
63+
CLASS ltc_loop_at DEFINITION INHERITING FROM ltc_read_table FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
64+
PROTECTED SECTION.
65+
METHODS get_code_with_issue REDEFINITION.
66+
METHODS get_code_with_exemption REDEFINITION.
67+
ENDCLASS.
68+
69+
CLASS ltc_loop_at IMPLEMENTATION.
70+
71+
METHOD get_code_with_issue.
72+
result = VALUE #(
73+
( 'REPORT y_example. ' )
74+
75+
( ' START-OF-SELECTION. ' )
76+
( ' DATA tadir TYPE TABLE OF tadir. ' )
77+
( ' DATA exists TYPE abap_bool. ' )
78+
79+
( | LOOP AT tadir TRANSPORTING NO FIELDS WHERE object = 'y_check_prefer_line_exists'. | )
80+
( ' exists = abap_true. ' )
81+
( ' ENDLOOP. ' )
82+
).
83+
ENDMETHOD.
84+
85+
METHOD get_code_with_exemption.
86+
result = VALUE #(
87+
( 'REPORT y_example. ' )
88+
89+
( ' START-OF-SELECTION. ' )
90+
( ' DATA tadir TYPE TABLE OF tadir. ' )
91+
( ' DATA exists TYPE abap_bool. ' )
92+
93+
( | LOOP AT tadir TRANSPORTING NO FIELDS WHERE object = 'y_check_prefer_line_exists'. "#EC PREF_LINE_EX | )
94+
( ' exists = abap_true. ' )
95+
( ' ENDLOOP. ' )
96+
).
97+
ENDMETHOD.
98+
99+
ENDCLASS.
100+
101+
102+
103+
CLASS ltc_loop_at_from_to DEFINITION INHERITING FROM ltc_loop_at FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
104+
PROTECTED SECTION.
105+
METHODS get_code_with_issue REDEFINITION.
106+
METHODS get_code_without_issue REDEFINITION.
107+
ENDCLASS.
108+
109+
CLASS ltc_loop_at_from_to IMPLEMENTATION.
110+
111+
METHOD get_code_with_issue.
112+
result = VALUE #(
113+
( 'REPORT y_example. ' )
114+
115+
( ' START-OF-SELECTION. ' )
116+
( ' DATA from_list TYPE TABLE OF tadir. ' )
117+
( ' DATA exists TYPE abap_bool. ' )
118+
119+
( | LOOP AT from_list TRANSPORTING NO FIELDS WHERE object = 'y_check_prefer_line_exists'. | )
120+
( ' exists = abap_true. ' )
121+
( ' ENDLOOP. ' )
122+
).
123+
ENDMETHOD.
124+
125+
METHOD get_code_without_issue.
126+
result = VALUE #(
127+
( 'REPORT y_example. ' )
128+
129+
( ' START-OF-SELECTION. ' )
130+
( ' DATA tadir TYPE TABLE OF tadir. ' )
131+
( ' DATA exists TYPE abap_bool. ' )
132+
133+
( ' LOOP AT tadir TRANSPORTING NO FIELDS ' )
134+
( ' FROM 1 TO 50 ' )
135+
( | WHERE object = 'y_check_prefer_line_exists'. | )
136+
( ' exists = abap_true. ' )
137+
( ' ENDLOOP. ' )
138+
).
139+
ENDMETHOD.
140+
141+
ENDCLASS.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
3+
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
4+
<asx:values>
5+
<VSEOCLASS>
6+
<CLSNAME>Y_CHECK_PREFER_LINE_EXISTS</CLSNAME>
7+
<LANGU>E</LANGU>
8+
<DESCRIPT>Prefer LINE_EXISTS or LINE_INDEX to READ TABLE or LOOP AT</DESCRIPT>
9+
<STATE>1</STATE>
10+
<CLSCCINCL>X</CLSCCINCL>
11+
<FIXPT>X</FIXPT>
12+
<UNICODE>X</UNICODE>
13+
<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
14+
</VSEOCLASS>
15+
</asx:values>
16+
</asx:abap>
17+
</abapGit>

src/examples/y_demo_failures.clas.abap

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ CLASS y_demo_failures DEFINITION PUBLIC FINAL CREATE PUBLIC.
115115
METHODS prefer_is_not_to_not_is.
116116
METHODS prefer_case_to_elseif.
117117
METHODS prefer_new_to_create_object.
118+
METHODS prefer_line_exists.
118119
METHODS deprecated_classes.
119120
METHODS scope_of_variable.
120121

@@ -416,6 +417,16 @@ CLASS Y_DEMO_FAILURES IMPLEMENTATION.
416417
ENDMETHOD.
417418

418419

420+
METHOD prefer_line_exists.
421+
DATA tadir TYPE TABLE OF tadir.
422+
DATA exists TYPE abap_bool.
423+
READ TABLE tadir TRANSPORTING NO FIELDS WITH KEY object = 'y_demo_failures'.
424+
IF sy-subrc = 0.
425+
exists = abap_true.
426+
ENDIF.
427+
ENDMETHOD.
428+
429+
419430
METHOD pseudo_comment_usage.
420431
ENDMETHOD. "#EC EMPTY_PROCEDURE
421432

0 commit comments

Comments
 (0)