@abaplint/core 2.79.19 → 2.79.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +6 -6
  2. package/build/abaplint.d.ts +5 -0
  3. package/build/src/abap/2_statements/expressions/index.js +1 -0
  4. package/build/src/abap/2_statements/expressions/reduce_body.js +2 -2
  5. package/build/src/abap/2_statements/expressions/reduce_next.js +13 -0
  6. package/build/src/abap/5_syntax/_builtin.js +50 -50
  7. package/build/src/abap/5_syntax/basic_types.js +7 -1
  8. package/build/src/abap/5_syntax/expressions/method_source.js +8 -2
  9. package/build/src/abap/5_syntax/expressions/reduce_body.js +4 -0
  10. package/build/src/abap/5_syntax/expressions/reduce_next.js +17 -0
  11. package/build/src/abap/5_syntax/statements/tables.js +1 -1
  12. package/build/src/ddic.js +6 -6
  13. package/build/src/lsp/_lookup.js +13 -11
  14. package/build/src/lsp/help.js +7 -7
  15. package/build/src/objects/assignment_service_to_authorization_group.js +1 -1
  16. package/build/src/registry.js +1 -1
  17. package/build/src/rules/abapdoc.js +1 -1
  18. package/build/src/rules/ambiguous_statement.js +5 -5
  19. package/build/src/rules/avoid_use.js +6 -6
  20. package/build/src/rules/begin_end_names.js +4 -4
  21. package/build/src/rules/begin_single_include.js +12 -12
  22. package/build/src/rules/chain_mainly_declarations.js +4 -4
  23. package/build/src/rules/check_abstract.js +2 -2
  24. package/build/src/rules/check_comments.js +3 -3
  25. package/build/src/rules/check_include.js +3 -3
  26. package/build/src/rules/check_no_handler_pragma.js +8 -8
  27. package/build/src/rules/check_subrc.js +8 -8
  28. package/build/src/rules/commented_code.js +1 -1
  29. package/build/src/rules/constructor_visibility_public.js +4 -4
  30. package/build/src/rules/contains_tab.js +2 -2
  31. package/build/src/rules/downport.js +200 -45
  32. package/build/src/rules/exit_or_check.js +3 -3
  33. package/build/src/rules/exporting.js +1 -1
  34. package/build/src/rules/forbidden_identifier.js +1 -1
  35. package/build/src/rules/forbidden_void_type.js +2 -2
  36. package/build/src/rules/functional_writing.js +17 -17
  37. package/build/src/rules/global_class.js +4 -4
  38. package/build/src/rules/identical_conditions.js +2 -2
  39. package/build/src/rules/identical_contents.js +15 -15
  40. package/build/src/rules/identical_descriptions.js +4 -4
  41. package/build/src/rules/if_in_if.js +7 -7
  42. package/build/src/rules/implement_methods.js +3 -3
  43. package/build/src/rules/in_statement_indentation.js +11 -11
  44. package/build/src/rules/intf_referencing_clas.js +3 -3
  45. package/build/src/rules/line_break_style.js +2 -2
  46. package/build/src/rules/line_length.js +1 -1
  47. package/build/src/rules/line_only_punc.js +1 -1
  48. package/build/src/rules/local_variable_names.js +2 -2
  49. package/build/src/rules/many_parentheses.js +10 -10
  50. package/build/src/rules/max_one_method_parameter_per_line.js +7 -7
  51. package/build/src/rules/max_one_statement.js +3 -3
  52. package/build/src/rules/nesting.js +1 -1
  53. package/build/src/rules/no_public_attributes.js +1 -1
  54. package/build/src/rules/no_yoda_conditions.js +4 -4
  55. package/build/src/rules/obsolete_statement.js +36 -36
  56. package/build/src/rules/omit_parameter_name.js +3 -3
  57. package/build/src/rules/omit_receiving.js +13 -13
  58. package/build/src/rules/parser_702_chaining.js +2 -2
  59. package/build/src/rules/parser_error.js +2 -2
  60. package/build/src/rules/parser_missing_space.js +1 -1
  61. package/build/src/rules/prefer_inline.js +16 -16
  62. package/build/src/rules/prefer_is_not.js +7 -7
  63. package/build/src/rules/prefer_raise_exception_new.js +3 -3
  64. package/build/src/rules/prefer_returning_to_exporting.js +1 -1
  65. package/build/src/rules/prefer_xsdbool.js +2 -2
  66. package/build/src/rules/remove_descriptions.js +4 -4
  67. package/build/src/rules/rfc_error_handling.js +9 -9
  68. package/build/src/rules/select_add_order_by.js +5 -5
  69. package/build/src/rules/select_performance.js +2 -2
  70. package/build/src/rules/sicf_consistency.js +4 -4
  71. package/build/src/rules/space_before_dot.js +2 -2
  72. package/build/src/rules/start_at_tab.js +1 -1
  73. package/build/src/rules/sy_modification.js +2 -2
  74. package/build/src/rules/tabl_enhancement_category.js +2 -2
  75. package/build/src/rules/unused_methods.js +9 -9
  76. package/build/src/rules/unused_variables.js +6 -6
  77. package/build/src/rules/use_bool_expression.js +8 -8
  78. package/build/src/rules/use_line_exists.js +6 -6
  79. package/build/src/rules/use_new.js +2 -2
  80. package/build/src/rules/when_others_last.js +6 -6
  81. package/package.json +76 -76
@@ -34,14 +34,14 @@ class CheckSubrc extends _abap_rule_1.ABAPRule {
34
34
  key: "check_subrc",
35
35
  title: "Check sy-subrc",
36
36
  shortDescription: `Check sy-subrc`,
37
- extendedInformation: `Pseudo comment "#EC CI_SUBRC can be added to suppress findings
38
-
39
- If sy-dbcnt is checked after database statements, it is considered okay.
40
-
41
- "SELECT SINGLE @abap_true FROM " is considered as an existence check
42
-
43
- If IS ASSIGNED is checked after assigning, it is considered okay.
44
-
37
+ extendedInformation: `Pseudo comment "#EC CI_SUBRC can be added to suppress findings
38
+
39
+ If sy-dbcnt is checked after database statements, it is considered okay.
40
+
41
+ "SELECT SINGLE @abap_true FROM " is considered as an existence check
42
+
43
+ If IS ASSIGNED is checked after assigning, it is considered okay.
44
+
45
45
  FIND statement with MATCH COUNT is considered okay if subrc is not checked`,
46
46
  tags: [_irule_1.RuleTag.SingleFile],
47
47
  pseudoComment: "EC CI_SUBRC",
@@ -30,7 +30,7 @@ class CommentedCode extends _abap_rule_1.ABAPRule {
30
30
  key: "commented_code",
31
31
  title: "Find commented code",
32
32
  shortDescription: `Detects usage of commented out code.`,
33
- extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#delete-code-instead-of-commenting-it
33
+ extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#delete-code-instead-of-commenting-it
34
34
  https://docs.abapopenchecks.org/checks/14/`,
35
35
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
36
36
  };
@@ -18,10 +18,10 @@ class ConstructorVisibilityPublic {
18
18
  key: "constructor_visibility_public",
19
19
  title: "Check constructor visibility is public",
20
20
  shortDescription: `Constructor must be placed in the public section, even if the class is not CREATE PUBLIC.`,
21
- extendedInformation: `
22
- This only applies to global classes.
23
-
24
- https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#if-your-global-class-is-create-private-leave-the-constructor-public
21
+ extendedInformation: `
22
+ This only applies to global classes.
23
+
24
+ https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#if-your-global-class-is-create-private-leave-the-constructor-public
25
25
  https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abeninstance_constructor_guidl.htm`,
26
26
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
27
27
  };
@@ -25,8 +25,8 @@ class ContainsTab extends _abap_rule_1.ABAPRule {
25
25
  key: "contains_tab",
26
26
  title: "Code contains tab",
27
27
  shortDescription: `Checks for usage of tabs (enable to enforce spaces)`,
28
- extendedInformation: `
29
- https://docs.abapopenchecks.org/checks/09/
28
+ extendedInformation: `
29
+ https://docs.abapopenchecks.org/checks/09/
30
30
  https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#indent-and-snap-to-tab`,
31
31
  tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
32
32
  };
@@ -5,6 +5,7 @@ const _basic_rule_config_1 = require("./_basic_rule_config");
5
5
  const issue_1 = require("../issue");
6
6
  const _irule_1 = require("./_irule");
7
7
  const _statement_1 = require("../abap/2_statements/statements/_statement");
8
+ const nodes_1 = require("../abap/nodes");
8
9
  const Statements = require("../abap/2_statements/statements");
9
10
  const Expressions = require("../abap/2_statements/expressions");
10
11
  const edit_helper_1 = require("../edit_helper");
@@ -18,6 +19,7 @@ const _typed_identifier_1 = require("../abap/types/_typed_identifier");
18
19
  const basic_1 = require("../abap/types/basic");
19
20
  const config_1 = require("../config");
20
21
  const tokens_1 = require("../abap/1_lexer/tokens");
22
+ // todo: refactor each sub-rule to new classes
21
23
  class DownportConf extends _basic_rule_config_1.BasicRuleConfig {
22
24
  }
23
25
  exports.DownportConf = DownportConf;
@@ -30,25 +32,27 @@ class Downport {
30
32
  key: "downport",
31
33
  title: "Downport statement",
32
34
  shortDescription: `Experimental downport functionality`,
33
- extendedInformation: `Much like the 'commented_code' rule this rule loops through unknown statements and tries parsing with
34
- a higher level language version. If successful, various rules are applied to downport the statement.
35
- Target downport version is always v702, thus rule is only enabled if target version is v702.
36
-
37
- Current rules:
38
- * NEW transformed to CREATE OBJECT, opposite of https://rules.abaplint.org/use_new/
39
- * DATA() definitions are outlined, opposite of https://rules.abaplint.org/prefer_inline/
40
- * FIELD-SYMBOL() definitions are outlined
41
- * CONV is outlined
42
- * EMPTY KEY is changed to DEFAULT KEY, opposite of DEFAULT KEY in https://rules.abaplint.org/avoid_use/
43
- * CAST changed to ?=
44
- * LOOP AT method_call( ) is outlined
45
- * VALUE # with structure fields
46
- * VALUE # with internal table lines
47
- * Table Expressions[ index ] are outlined
48
- * SELECT INTO @DATA definitions are outlined
49
- * Some occurrences of string template formatting option ALPHA changed to function module call
50
- * SELECT/INSERT/MODIFY/DELETE/UPDATE "," in field list removed, "@" in source/targets removed
51
-
35
+ extendedInformation: `Much like the 'commented_code' rule this rule loops through unknown statements and tries parsing with
36
+ a higher level language version. If successful, various rules are applied to downport the statement.
37
+ Target downport version is always v702, thus rule is only enabled if target version is v702.
38
+
39
+ Current rules:
40
+ * NEW transformed to CREATE OBJECT, opposite of https://rules.abaplint.org/use_new/
41
+ * DATA() definitions are outlined, opposite of https://rules.abaplint.org/prefer_inline/
42
+ * FIELD-SYMBOL() definitions are outlined
43
+ * CONV is outlined
44
+ * COND is outlined
45
+ * REDUCE is outlined
46
+ * EMPTY KEY is changed to DEFAULT KEY, opposite of DEFAULT KEY in https://rules.abaplint.org/avoid_use/
47
+ * CAST changed to ?=
48
+ * LOOP AT method_call( ) is outlined
49
+ * VALUE # with structure fields
50
+ * VALUE # with internal table lines
51
+ * Table Expressions[ index ] are outlined
52
+ * SELECT INTO @DATA definitions are outlined
53
+ * Some occurrences of string template formatting option ALPHA changed to function module call
54
+ * SELECT/INSERT/MODIFY/DELETE/UPDATE "," in field list removed, "@" in source/targets removed
55
+
52
56
  Only one transformation is applied to a statement at a time, so multiple steps might be required to do the full downport.`,
53
57
  tags: [_irule_1.RuleTag.Experimental, _irule_1.RuleTag.Downport, _irule_1.RuleTag.Quickfix],
54
58
  };
@@ -167,6 +171,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
167
171
  if (found) {
168
172
  return found;
169
173
  }
174
+ found = this.outlineReduce(high, lowFile, highSyntax);
175
+ if (found) {
176
+ return found;
177
+ }
170
178
  found = this.outlineCast(high, lowFile, highSyntax);
171
179
  if (found) {
172
180
  return found;
@@ -175,6 +183,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
175
183
  if (found) {
176
184
  return found;
177
185
  }
186
+ found = this.outlineCond(high, lowFile, highSyntax);
187
+ if (found) {
188
+ return found;
189
+ }
178
190
  found = this.outlineDataSimple(high, lowFile);
179
191
  if (found) {
180
192
  return found;
@@ -294,10 +306,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
294
306
  const fieldName = f.concatTokens();
295
307
  fieldDefinition += indentation + " " + fieldName + " TYPE " + tableName + "-" + fieldName + ",\n";
296
308
  }
297
- fieldDefinition = `DATA: BEGIN OF ${name},
309
+ fieldDefinition = `DATA: BEGIN OF ${name},
298
310
  ${fieldDefinition}${indentation} END OF ${name}.`;
299
311
  }
300
- const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `${fieldDefinition}
312
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `${fieldDefinition}
301
313
  ${indentation}`);
302
314
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inlineData.getFirstToken().getStart(), inlineData.getLastToken().getEnd(), name);
303
315
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
@@ -333,9 +345,9 @@ ${indentation}`);
333
345
  }
334
346
  const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
335
347
  const name = ((_c = inlineData.findFirstExpression(Expressions.TargetField)) === null || _c === void 0 ? void 0 : _c.concatTokens()) || "error";
336
- const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `TYPES: BEGIN OF ${uniqueName},
337
- ${fieldDefinitions}${indentation} END OF ${uniqueName}.
338
- ${indentation}DATA ${name} TYPE STANDARD TABLE OF ${uniqueName} WITH DEFAULT KEY.
348
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `TYPES: BEGIN OF ${uniqueName},
349
+ ${fieldDefinitions}${indentation} END OF ${uniqueName}.
350
+ ${indentation}DATA ${name} TYPE STANDARD TABLE OF ${uniqueName} WITH DEFAULT KEY.
339
351
  ${indentation}`);
340
352
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inlineData.getFirstToken().getStart(), inlineData.getLastToken().getEnd(), name);
341
353
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
@@ -369,11 +381,11 @@ ${indentation}`);
369
381
  const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
370
382
  const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
371
383
  const firstToken = node.getFirstToken();
372
- const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
373
- ${indentation}READ TABLE ${pre} INDEX ${(_a = tableExpression.findFirstExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens()} INTO ${uniqueName}.
374
- ${indentation}IF sy-subrc <> 0.
375
- ${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
376
- ${indentation}ENDIF.
384
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
385
+ ${indentation}READ TABLE ${pre} INDEX ${(_a = tableExpression.findFirstExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens()} INTO ${uniqueName}.
386
+ ${indentation}IF sy-subrc <> 0.
387
+ ${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
388
+ ${indentation}ENDIF.
377
389
  ${indentation}`);
378
390
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, startToken.getStart(), tableExpression.getLastToken().getEnd(), uniqueName);
379
391
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
@@ -424,7 +436,7 @@ ${indentation}`);
424
436
  }
425
437
  i = key;
426
438
  const concat = i.concatTokens();
427
- if (concat.includes("WITH EMPTY KEY") === false) {
439
+ if (concat.toUpperCase().includes("WITH EMPTY KEY") === false) {
428
440
  continue;
429
441
  }
430
442
  const token = i.findDirectTokenByText("EMPTY");
@@ -472,10 +484,10 @@ ${indentation}`);
472
484
  const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
473
485
  const source = (_b = templateSource === null || templateSource === void 0 ? void 0 : templateSource.findDirectExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.concatTokens();
474
486
  const topTarget = (_c = node.findDirectExpression(Expressions.Target)) === null || _c === void 0 ? void 0 : _c.concatTokens();
475
- const code = `CALL FUNCTION '${functionName}'
476
- ${indentation} EXPORTING
477
- ${indentation} input = ${source}
478
- ${indentation} IMPORTING
487
+ const code = `CALL FUNCTION '${functionName}'
488
+ ${indentation} EXPORTING
489
+ ${indentation} input = ${source}
490
+ ${indentation} IMPORTING
479
491
  ${indentation} output = ${topTarget}.`;
480
492
  const fix = edit_helper_1.EditHelper.replaceRange(lowFile, node.getFirstToken().getStart(), node.getLastToken().getEnd(), code);
481
493
  return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Downport ALPHA", this.getMetadata().key, this.conf.severity, fix);
@@ -535,6 +547,66 @@ ${indentation} output = ${topTarget}.`;
535
547
  }
536
548
  return undefined;
537
549
  }
550
+ outlineReduce(node, lowFile, highSyntax) {
551
+ var _a, _b, _c;
552
+ for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
553
+ const firstToken = i.getFirstToken();
554
+ if (firstToken.getStr().toUpperCase() !== "REDUCE") {
555
+ continue;
556
+ }
557
+ const type = this.findType(i, lowFile, highSyntax);
558
+ if (type === undefined) {
559
+ continue;
560
+ }
561
+ const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
562
+ const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
563
+ let body = "";
564
+ let name = "";
565
+ const reduceBody = i.findDirectExpression(Expressions.ReduceBody);
566
+ if (reduceBody === undefined) {
567
+ continue;
568
+ }
569
+ for (const init of reduceBody.findDirectExpressions(Expressions.InlineFieldDefinition)) {
570
+ name = init.getFirstToken().getStr();
571
+ body += indentation + `DATA(${name}) = ${(_a = reduceBody.findFirstExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens()}.\n`;
572
+ }
573
+ const loop = reduceBody.findFirstExpression(Expressions.InlineLoopDefinition);
574
+ if (loop === undefined) {
575
+ continue;
576
+ }
577
+ const loopSource = (_b = loop.findFirstExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.concatTokens();
578
+ const loopTarget = (_c = loop.findFirstExpression(Expressions.TargetField)) === null || _c === void 0 ? void 0 : _c.concatTokens();
579
+ body += indentation + `LOOP AT ${loopSource} INTO DATA(${loopTarget}).\n`;
580
+ const next = reduceBody.findDirectExpression(Expressions.ReduceNext);
581
+ if (next === undefined) {
582
+ continue;
583
+ }
584
+ for (const n of next.getChildren()) {
585
+ if (n.concatTokens().toUpperCase() === "NEXT") {
586
+ continue;
587
+ }
588
+ else if (n.concatTokens() === "=") {
589
+ body += " = ";
590
+ }
591
+ else if (n.get() instanceof Expressions.Field) {
592
+ body += indentation + " " + n.concatTokens();
593
+ }
594
+ else if (n.get() instanceof Expressions.Source) {
595
+ body += n.concatTokens() + ".\n";
596
+ }
597
+ }
598
+ body += indentation + `ENDLOOP.\n`;
599
+ body += indentation + `${uniqueName} = ${name}.\n`;
600
+ const abap = `DATA ${uniqueName} TYPE ${type}.\n` +
601
+ body +
602
+ indentation;
603
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), abap);
604
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, firstToken.getStart(), i.getLastToken().getEnd(), uniqueName);
605
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
606
+ return issue_1.Issue.atToken(lowFile, firstToken, "Downport REDUCE", this.getMetadata().key, this.conf.severity, fix);
607
+ }
608
+ return undefined;
609
+ }
538
610
  outlineValue(node, lowFile, highSyntax) {
539
611
  var _a;
540
612
  for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
@@ -564,10 +636,13 @@ ${indentation} output = ${topTarget}.`;
564
636
  }
565
637
  body += indentation + structureName + "-" + b.concatTokens() + ".\n";
566
638
  }
567
- if (b.get() instanceof Expressions.Source) {
639
+ else if (b.get() instanceof Expressions.Source) {
568
640
  structureName = b.concatTokens();
569
641
  }
570
- if (b.concatTokens() === ")") {
642
+ else if (b instanceof nodes_1.ExpressionNode && b.get() instanceof Expressions.Let) {
643
+ body += this.outlineLet(b, indentation, highSyntax, lowFile);
644
+ }
645
+ else if (b.concatTokens() === ")") {
571
646
  body += indentation + `APPEND ${structureName} TO ${uniqueName}.\n`;
572
647
  }
573
648
  }
@@ -581,13 +656,40 @@ ${indentation} output = ${topTarget}.`;
581
656
  }
582
657
  return undefined;
583
658
  }
659
+ outlineLet(node, indentation, highSyntax, lowFile) {
660
+ var _a;
661
+ let ret = "";
662
+ for (const f of node.findDirectExpressions(Expressions.InlineFieldDefinition)) {
663
+ const c = f.getFirstChild();
664
+ if (c === undefined) {
665
+ continue;
666
+ }
667
+ const name = c.concatTokens().toLowerCase();
668
+ const spag = highSyntax.spaghetti.lookupPosition(c.getFirstToken().getStart(), lowFile.getFilename());
669
+ if (spag === undefined) {
670
+ continue;
671
+ }
672
+ const found = spag.findVariable(name);
673
+ if (found === undefined) {
674
+ continue;
675
+ }
676
+ const type = found.getType().getQualifiedName() ? (_a = found.getType().getQualifiedName()) === null || _a === void 0 ? void 0 : _a.toLowerCase() : found.getType().toABAP();
677
+ ret += indentation + "DATA " + name + ` TYPE ${type}.\n`;
678
+ const source = node.findFirstExpression(Expressions.Source);
679
+ if (source) {
680
+ ret += indentation + name + ` = ${source.concatTokens()}.\n`;
681
+ }
682
+ }
683
+ return ret;
684
+ }
584
685
  findType(i, lowFile, highSyntax) {
686
+ var _a;
585
687
  const expr = i.findDirectExpression(Expressions.TypeNameOrInfer);
586
688
  if (expr === undefined) {
587
689
  return undefined;
588
690
  }
589
691
  const firstToken = expr.getFirstToken();
590
- const concat = expr.concatTokens();
692
+ const concat = expr.concatTokens().toLowerCase();
591
693
  if (concat !== "#") {
592
694
  return concat;
593
695
  }
@@ -608,10 +710,10 @@ ${indentation} output = ${topTarget}.`;
608
710
  if (inferred === undefined) {
609
711
  return undefined;
610
712
  }
611
- return inferred.getType().getQualifiedName();
713
+ return (_a = inferred.getType().getQualifiedName()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
612
714
  }
613
715
  outlineFS(node, lowFile, highSyntax) {
614
- var _a;
716
+ var _a, _b;
615
717
  for (const i of node.findAllExpressionsRecursive(Expressions.InlineFS)) {
616
718
  const nameToken = (_a = i.findDirectExpression(Expressions.TargetFieldSymbol)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
617
719
  if (nameToken === undefined) {
@@ -629,7 +731,7 @@ ${indentation} output = ${topTarget}.`;
629
731
  else if (found.getType() instanceof basic_1.VoidType) {
630
732
  return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Error outlining voided type", this.getMetadata().key, this.conf.severity);
631
733
  }
632
- const type = found.getType().getQualifiedName() ? found.getType().getQualifiedName() : found.getType().toABAP();
734
+ const type = found.getType().getQualifiedName() ? (_b = found.getType().getQualifiedName()) === null || _b === void 0 ? void 0 : _b.toLowerCase() : found.getType().toABAP();
633
735
  const code = `FIELD-SYMBOLS ${name} TYPE ${type}.\n` +
634
736
  " ".repeat(node.getFirstToken().getStart().getCol() - 1);
635
737
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
@@ -640,7 +742,7 @@ ${indentation} output = ${topTarget}.`;
640
742
  return undefined;
641
743
  }
642
744
  outlineData(node, lowFile, highSyntax) {
643
- var _a;
745
+ var _a, _b;
644
746
  for (const i of node.findAllExpressionsRecursive(Expressions.InlineData)) {
645
747
  const nameToken = (_a = i.findDirectExpression(Expressions.TargetField)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
646
748
  if (nameToken === undefined) {
@@ -658,7 +760,7 @@ ${indentation} output = ${topTarget}.`;
658
760
  else if (found.getType() instanceof basic_1.VoidType) {
659
761
  return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Error outlining voided type", this.getMetadata().key, this.conf.severity);
660
762
  }
661
- const type = found.getType().getQualifiedName() ? found.getType().getQualifiedName() : found.getType().toABAP();
763
+ const type = found.getType().getQualifiedName() ? (_b = found.getType().getQualifiedName()) === null || _b === void 0 ? void 0 : _b.toLowerCase() : found.getType().toABAP();
662
764
  const code = `DATA ${name} TYPE ${type}.\n` +
663
765
  " ".repeat(node.getFirstToken().getStart().getCol() - 1);
664
766
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
@@ -668,6 +770,59 @@ ${indentation} output = ${topTarget}.`;
668
770
  }
669
771
  return undefined;
670
772
  }
773
+ outlineCond(node, lowFile, highSyntax) {
774
+ for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
775
+ if (i.getFirstToken().getStr().toUpperCase() !== "COND") {
776
+ continue;
777
+ }
778
+ const body = i.findDirectExpression(Expressions.CondBody);
779
+ if (body === undefined) {
780
+ continue;
781
+ }
782
+ const uniqueName = this.uniqueName(i.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
783
+ const type = this.findType(i, lowFile, highSyntax);
784
+ const indent = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
785
+ const bodyCode = this.buildCondBody(body, uniqueName, indent);
786
+ const abap = `DATA ${uniqueName} TYPE ${type}.\n` + bodyCode;
787
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), abap);
788
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, i.getFirstToken().getStart(), i.getLastToken().getEnd(), uniqueName);
789
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
790
+ return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Downport COND", this.getMetadata().key, this.conf.severity, fix);
791
+ }
792
+ return undefined;
793
+ }
794
+ buildCondBody(body, uniqueName, indent) {
795
+ let code = indent;
796
+ for (const c of body.getChildren()) {
797
+ if (c instanceof nodes_1.TokenNode) {
798
+ switch (c.getFirstToken().getStr().toUpperCase()) {
799
+ case "WHEN":
800
+ code += "IF ";
801
+ break;
802
+ case "THEN":
803
+ code += ".\n";
804
+ break;
805
+ case "ELSE":
806
+ code += indent + "ELSE.\n";
807
+ break;
808
+ default:
809
+ throw "buildCondBody, unexpected token";
810
+ }
811
+ }
812
+ else if (c.get() instanceof Expressions.Cond) {
813
+ code += c.concatTokens();
814
+ }
815
+ else if (c.get() instanceof Expressions.Source) {
816
+ code += indent + " " + uniqueName + " = " + c.concatTokens() + ".\n";
817
+ }
818
+ else {
819
+ throw "buildCondBody, unexpected expression";
820
+ }
821
+ }
822
+ code += indent + "ENDIF.\n";
823
+ code += indent;
824
+ return code;
825
+ }
671
826
  outlineConv(node, lowFile, highSyntax) {
672
827
  var _a;
673
828
  for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
@@ -680,10 +835,10 @@ ${indentation} output = ${topTarget}.`;
680
835
  }
681
836
  const uniqueName = this.uniqueName(i.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
682
837
  const type = this.findType(i, lowFile, highSyntax);
838
+ const indent = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
683
839
  const abap = `DATA ${uniqueName} TYPE ${type}.\n` +
684
- " ".repeat(node.getFirstToken().getStart().getCol() - 1) +
685
- `${uniqueName} = ${body}.\n` +
686
- " ".repeat(node.getFirstToken().getStart().getCol() - 1);
840
+ indent + `${uniqueName} = ${body}.\n` +
841
+ indent;
687
842
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), abap);
688
843
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, i.getFirstToken().getStart(), i.getLastToken().getEnd(), uniqueName);
689
844
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
@@ -24,10 +24,10 @@ class ExitOrCheck extends _abap_rule_1.ABAPRule {
24
24
  return {
25
25
  key: "exit_or_check",
26
26
  title: "Find EXIT or CHECK outside loops",
27
- shortDescription: `Detects usages of EXIT or CHECK statements outside of loops.
27
+ shortDescription: `Detects usages of EXIT or CHECK statements outside of loops.
28
28
  Use RETURN to leave procesing blocks instead.`,
29
- extendedInformation: `https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenleave_processing_blocks.htm
30
- https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapcheck_processing_blocks.htm
29
+ extendedInformation: `https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenleave_processing_blocks.htm
30
+ https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapcheck_processing_blocks.htm
31
31
  https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#check-vs-return`,
32
32
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
33
33
  };
@@ -22,7 +22,7 @@ class Exporting extends _abap_rule_1.ABAPRule {
22
22
  shortDescription: `Detects EXPORTING statements which can be omitted.`,
23
23
  badExample: `call_method( EXPORTING foo = bar ).`,
24
24
  goodExample: `call_method( foo = bar ).`,
25
- extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#omit-the-optional-keyword-exporting
25
+ extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#omit-the-optional-keyword-exporting
26
26
  https://docs.abapopenchecks.org/checks/30/`,
27
27
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
28
28
  };
@@ -26,7 +26,7 @@ class ForbiddenIdentifier extends _abap_rule_1.ABAPRule {
26
26
  key: "forbidden_identifier",
27
27
  title: "Forbidden Identifier",
28
28
  shortDescription: `Forbid use of specified identifiers, list of regex.`,
29
- extendedInformation: `Used in the transpiler to find javascript keywords in ABAP identifiers,
29
+ extendedInformation: `Used in the transpiler to find javascript keywords in ABAP identifiers,
30
30
  https://github.com/abaplint/transpiler/blob/bda94b8b56e2b7f2f87be2168f12361aa530220e/packages/transpiler/src/validation.ts#L44`,
31
31
  tags: [_irule_1.RuleTag.SingleFile],
32
32
  };
@@ -28,8 +28,8 @@ class ForbiddenVoidType {
28
28
  key: "forbidden_void_type",
29
29
  title: "Forbidden Void Types",
30
30
  shortDescription: `Avoid usage of specified void types.`,
31
- extendedInformation: `Inspiration:
32
- BOOLEAN, BOOLE_D, CHAR01, CHAR1, CHAR10, CHAR12, CHAR128, CHAR2, CHAR20, CHAR4, CHAR70,
31
+ extendedInformation: `Inspiration:
32
+ BOOLEAN, BOOLE_D, CHAR01, CHAR1, CHAR10, CHAR12, CHAR128, CHAR2, CHAR20, CHAR4, CHAR70,
33
33
  DATS, TIMS, DATUM, FLAG, INT4, NUMC3, NUMC4, SAP_BOOL, TEXT25, TEXT80, X255, XFELD`,
34
34
  };
35
35
  }
@@ -28,26 +28,26 @@ class FunctionalWriting extends _abap_rule_1.ABAPRule {
28
28
  key: "functional_writing",
29
29
  title: "Use functional writing",
30
30
  shortDescription: `Detects usage of call method when functional style calls can be used.`,
31
- extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-functional-to-procedural-calls
31
+ extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-functional-to-procedural-calls
32
32
  https://docs.abapopenchecks.org/checks/07/`,
33
33
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
34
- badExample: `CALL METHOD zcl_class=>method( ).
35
- CALL METHOD cl_abap_typedescr=>describe_by_name
36
- EXPORTING
37
- p_name = 'NAME'
38
- RECEIVING
39
- p_descr_ref = lr_typedescr
40
- EXCEPTIONS
41
- type_not_found = 1
34
+ badExample: `CALL METHOD zcl_class=>method( ).
35
+ CALL METHOD cl_abap_typedescr=>describe_by_name
36
+ EXPORTING
37
+ p_name = 'NAME'
38
+ RECEIVING
39
+ p_descr_ref = lr_typedescr
40
+ EXCEPTIONS
41
+ type_not_found = 1
42
42
  OTHERS = 2.`,
43
- goodExample: `zcl_class=>method( ).
44
- cl_abap_typedescr=>describe_by_name(
45
- EXPORTING
46
- p_name = 'NAME'
47
- RECEIVING
48
- p_descr_ref = lr_typedescr
49
- EXCEPTIONS
50
- type_not_found = 1
43
+ goodExample: `zcl_class=>method( ).
44
+ cl_abap_typedescr=>describe_by_name(
45
+ EXPORTING
46
+ p_name = 'NAME'
47
+ RECEIVING
48
+ p_descr_ref = lr_typedescr
49
+ EXCEPTIONS
50
+ type_not_found = 1
51
51
  OTHERS = 2 ).`,
52
52
  };
53
53
  }
@@ -18,10 +18,10 @@ class GlobalClass extends _abap_rule_1.ABAPRule {
18
18
  return {
19
19
  key: "global_class",
20
20
  title: "Global class checks",
21
- shortDescription: `Checks related to global classes.
22
- * global classes must be in own files
23
- * file names must match class name
24
- * global classes must be global definitions
21
+ shortDescription: `Checks related to global classes.
22
+ * global classes must be in own files
23
+ * file names must match class name
24
+ * global classes must be global definitions
25
25
  * global interfaces must be global definitions`,
26
26
  tags: [_irule_1.RuleTag.Syntax],
27
27
  };
@@ -33,8 +33,8 @@ class IdenticalConditions extends _abap_rule_1.ABAPRule {
33
33
  return {
34
34
  key: "identical_conditions",
35
35
  title: "Identical conditions",
36
- shortDescription: `Find identical conditions in IF + CASE + WHILE etc
37
-
36
+ shortDescription: `Find identical conditions in IF + CASE + WHILE etc
37
+
38
38
  Prerequsites: code is pretty printed with identical cAsE`,
39
39
  tags: [_irule_1.RuleTag.SingleFile],
40
40
  };
@@ -19,24 +19,24 @@ class IdenticalContents extends _abap_rule_1.ABAPRule {
19
19
  return {
20
20
  key: "identical_contents",
21
21
  title: "Identical contents",
22
- shortDescription: `Find identical contents in blocks inside IFs, both in the beginning and in the end.
23
-
24
- Prerequsites: code is pretty printed with identical cAsE
25
-
22
+ shortDescription: `Find identical contents in blocks inside IFs, both in the beginning and in the end.
23
+
24
+ Prerequsites: code is pretty printed with identical cAsE
25
+
26
26
  Chained statments are ignored`,
27
27
  tags: [_irule_1.RuleTag.SingleFile],
28
- badExample: `IF foo = bar.
29
- WRITE 'bar'.
30
- WRITE 'world'.
31
- ELSE.
32
- WRITE 'foo'.
33
- WRITE 'world'.
28
+ badExample: `IF foo = bar.
29
+ WRITE 'bar'.
30
+ WRITE 'world'.
31
+ ELSE.
32
+ WRITE 'foo'.
33
+ WRITE 'world'.
34
34
  ENDIF.`,
35
- goodExample: `IF foo = bar.
36
- WRITE 'bar'.
37
- ELSE.
38
- WRITE 'foo'.
39
- ENDIF.
35
+ goodExample: `IF foo = bar.
36
+ WRITE 'bar'.
37
+ ELSE.
38
+ WRITE 'foo'.
39
+ ENDIF.
40
40
  WRITE 'world'.`,
41
41
  };
42
42
  }
@@ -16,10 +16,10 @@ class IdenticalDescriptions {
16
16
  key: "identical_descriptions",
17
17
  title: "Identical descriptions",
18
18
  shortDescription: `Searches for objects with the same type and same description`,
19
- extendedInformation: `Case insensitive
20
-
21
- Only checks the master language descriptions
22
-
19
+ extendedInformation: `Case insensitive
20
+
21
+ Only checks the master language descriptions
22
+
23
23
  Works for: INTF, CLAS, DOMA, DTEL, FUNC in same FUGR`,
24
24
  tags: [],
25
25
  };
@@ -19,15 +19,15 @@ class IfInIf extends _abap_rule_1.ABAPRule {
19
19
  key: "if_in_if",
20
20
  title: "IF in IF",
21
21
  shortDescription: `Detects nested ifs which can be refactored to a single condition using AND.`,
22
- extendedInformation: `https://docs.abapopenchecks.org/checks/01/
22
+ extendedInformation: `https://docs.abapopenchecks.org/checks/01/
23
23
  https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#keep-the-nesting-depth-low`,
24
- badExample: `IF condition1.
25
- IF condition2.
26
- ...
27
- ENDIF.
24
+ badExample: `IF condition1.
25
+ IF condition2.
26
+ ...
27
+ ENDIF.
28
28
  ENDIF.`,
29
- goodExample: `IF ( condition1 ) AND ( condition2 ).
30
- ...
29
+ goodExample: `IF ( condition1 ) AND ( condition2 ).
30
+ ...
31
31
  ENDIF.`,
32
32
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
33
33
  };