@abaplint/core 2.120.71 → 2.120.72

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 (43) hide show
  1. package/build/abaplint.d.ts +10 -0
  2. package/build/src/abap/1_lexer/lexer.js +1 -1
  3. package/build/src/abap/1_lexer/lexer_stream.js +8 -0
  4. package/build/src/abap/3_structures/structures/_combi.js +61 -112
  5. package/build/src/abap/5_syntax/basic_types.js +4 -4
  6. package/build/src/abap/5_syntax/expressions/_check_database_work_area.js +127 -0
  7. package/build/src/abap/5_syntax/expressions/attribute_name.js +1 -1
  8. package/build/src/abap/5_syntax/expressions/conv_body.js +2 -2
  9. package/build/src/abap/5_syntax/expressions/field_chain.js +1 -1
  10. package/build/src/abap/5_syntax/expressions/form_param.js +1 -1
  11. package/build/src/abap/5_syntax/expressions/method_parameters.js +2 -2
  12. package/build/src/abap/5_syntax/expressions/select.js +1 -2
  13. package/build/src/abap/5_syntax/expressions/source.js +19 -3
  14. package/build/src/abap/5_syntax/statements/assign.js +15 -1
  15. package/build/src/abap/5_syntax/statements/insert_database.js +6 -3
  16. package/build/src/abap/5_syntax/statements/loop.js +1 -1
  17. package/build/src/abap/5_syntax/statements/modify_database.js +8 -4
  18. package/build/src/abap/5_syntax/statements/update_database.js +7 -3
  19. package/build/src/abap/5_syntax/statements/when_type.js +1 -1
  20. package/build/src/abap/types/class_attributes.js +1 -1
  21. package/build/src/cds/cds_lexer.js +1 -1
  22. package/build/src/lsp/help.js +2 -2
  23. package/build/src/objects/data_element.js +1 -1
  24. package/build/src/objects/table.js +1 -0
  25. package/build/src/registry.js +1 -1
  26. package/build/src/rules/align_pseudo_comments.js +1 -1
  27. package/build/src/rules/align_type_expressions.js +1 -1
  28. package/build/src/rules/change_if_to_case.js +1 -1
  29. package/build/src/rules/check_ddic.js +1 -1
  30. package/build/src/rules/class_attribute_names.js +1 -3
  31. package/build/src/rules/downport.js +122 -24
  32. package/build/src/rules/empty_structure.js +26 -0
  33. package/build/src/rules/functional_writing.js +1 -1
  34. package/build/src/rules/keyword_case.js +1 -1
  35. package/build/src/rules/local_class_naming.js +1 -1
  36. package/build/src/rules/many_parentheses.js +1 -1
  37. package/build/src/rules/obsolete_statement.js +3 -7
  38. package/build/src/rules/parser_missing_space.js +1 -2
  39. package/build/src/rules/prefer_raise_exception_new.js +2 -8
  40. package/build/src/rules/xml_consistency.js +16 -0
  41. package/build/src/utils/form_length_stats.js +1 -1
  42. package/build/src/utils/method_length_stats.js +1 -1
  43. package/package.json +1 -1
@@ -38,20 +38,23 @@ const Expressions = __importStar(require("../../2_statements/expressions"));
38
38
  const source_1 = require("../expressions/source");
39
39
  const dynamic_1 = require("../expressions/dynamic");
40
40
  const database_table_1 = require("../expressions/database_table");
41
+ const _check_database_work_area_1 = require("../expressions/_check_database_work_area");
41
42
  class InsertDatabase {
42
43
  runSyntax(node, input) {
44
+ const sourceTypes = new Map();
43
45
  for (const s of node.findAllExpressions(Expressions.Source)) {
44
- source_1.Source.runSyntax(s, input);
46
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
45
47
  }
46
48
  for (const s of node.findAllExpressions(Expressions.SimpleSource3)) {
47
- source_1.Source.runSyntax(s, input);
49
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
48
50
  }
49
51
  for (const d of node.findAllExpressions(Expressions.Dynamic)) {
50
52
  dynamic_1.Dynamic.runSyntax(d, input);
51
53
  }
52
54
  const dbtab = node.findFirstExpression(Expressions.DatabaseTable);
53
55
  if (dbtab !== undefined) {
54
- database_table_1.DatabaseTable.runSyntax(dbtab, input);
56
+ const dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, input);
57
+ (0, _check_database_work_area_1.checkDatabaseWorkArea)(node, dbSource, sourceTypes, input);
55
58
  }
56
59
  }
57
60
  }
@@ -130,7 +130,7 @@ class Loop {
130
130
  && topType instanceof basic_1.TableType) {
131
131
  // https://github.com/abap2xlsx/abap2xlsx/issues/1341
132
132
  const keyName = node.findExpressionAfterToken("KEY");
133
- let key = undefined;
133
+ let key;
134
134
  const name = (keyName === null || keyName === void 0 ? void 0 : keyName.get()) instanceof Expressions.SimpleName ? keyName.getFirstToken().getStr().toUpperCase() : undefined;
135
135
  // it might be dynamic, in that case we cannot check anything; and
136
136
  // primary_key is the predefined name of the primary key every table has
@@ -39,11 +39,13 @@ const dynamic_1 = require("../expressions/dynamic");
39
39
  const database_table_1 = require("../expressions/database_table");
40
40
  const source_1 = require("../expressions/source");
41
41
  const _reference_1 = require("../_reference");
42
+ const _check_database_work_area_1 = require("../expressions/_check_database_work_area");
42
43
  class ModifyDatabase {
43
44
  runSyntax(node, input) {
44
45
  for (const d of node.findAllExpressions(Expressions.Dynamic)) {
45
46
  dynamic_1.Dynamic.runSyntax(d, input);
46
47
  }
48
+ let dbSource = undefined;
47
49
  const dbtab = node.findFirstExpression(Expressions.DatabaseTable);
48
50
  if (dbtab !== undefined) {
49
51
  if (node.getChildren().length === 5) {
@@ -52,19 +54,21 @@ class ModifyDatabase {
52
54
  input.scope.addReference(dbtab.getFirstToken(), found, _reference_1.ReferenceType.DataWriteReference, input.filename);
53
55
  }
54
56
  else {
55
- database_table_1.DatabaseTable.runSyntax(dbtab, input);
57
+ dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, input);
56
58
  }
57
59
  }
58
60
  else {
59
- database_table_1.DatabaseTable.runSyntax(dbtab, input);
61
+ dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, input);
60
62
  }
61
63
  }
64
+ const sourceTypes = new Map();
62
65
  for (const s of node.findAllExpressions(Expressions.Source)) {
63
- source_1.Source.runSyntax(s, input);
66
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
64
67
  }
65
68
  for (const s of node.findAllExpressions(Expressions.SimpleSource3)) {
66
- source_1.Source.runSyntax(s, input);
69
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
67
70
  }
71
+ (0, _check_database_work_area_1.checkDatabaseWorkArea)(node, dbSource, sourceTypes, input);
68
72
  }
69
73
  }
70
74
  exports.ModifyDatabase = ModifyDatabase;
@@ -43,11 +43,13 @@ const identifier_1 = require("../../1_lexer/tokens/identifier");
43
43
  const database_table_1 = require("../expressions/database_table");
44
44
  const dynamic_1 = require("../expressions/dynamic");
45
45
  const _check_database_fields_1 = require("../expressions/_check_database_fields");
46
+ const _check_database_work_area_1 = require("../expressions/_check_database_work_area");
46
47
  class UpdateDatabase {
47
48
  runSyntax(node, input) {
49
+ let dbSource = undefined;
48
50
  const dbtab = node.findFirstExpression(Expressions.DatabaseTable);
49
51
  if (dbtab !== undefined) {
50
- const dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, input);
52
+ dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, input);
51
53
  const fields = node.findAllExpressions(Expressions.SQLFieldAndValue)
52
54
  .flatMap(fieldAndValue => fieldAndValue.findDirectExpressions(Expressions.SQLFieldName))
53
55
  .map(field => field.concatTokens().toUpperCase());
@@ -70,15 +72,17 @@ class UpdateDatabase {
70
72
  }
71
73
  }
72
74
  }
75
+ const sourceTypes = new Map();
73
76
  for (const s of node.findAllExpressions(Expressions.Source)) {
74
- source_1.Source.runSyntax(s, input);
77
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
75
78
  }
76
79
  for (const s of node.findAllExpressions(Expressions.SimpleSource3)) {
77
- source_1.Source.runSyntax(s, input);
80
+ sourceTypes.set(s, source_1.Source.runSyntax(s, input));
78
81
  }
79
82
  for (const d of node.findAllExpressions(Expressions.Dynamic)) {
80
83
  dynamic_1.Dynamic.runSyntax(d, input);
81
84
  }
85
+ (0, _check_database_work_area_1.checkDatabaseWorkArea)(node, dbSource, sourceTypes, input);
82
86
  if (input.scope.getType() === _scope_type_1.ScopeType.OpenSQL) {
83
87
  input.scope.pop(node.getLastToken().getEnd());
84
88
  }
@@ -47,7 +47,7 @@ class WhenType {
47
47
  if (nameToken === undefined) {
48
48
  return undefined;
49
49
  }
50
- let type = undefined;
50
+ let type;
51
51
  const className = nameToken.getStr().toUpperCase();
52
52
  const found = input.scope.existsObject(className);
53
53
  if (found === null || found === void 0 ? void 0 : found.id) {
@@ -318,7 +318,7 @@ class Attributes {
318
318
  return false;
319
319
  }
320
320
  parseAttribute(node, visibility, input) {
321
- let found = undefined;
321
+ let found;
322
322
  const s = node.get();
323
323
  if (s instanceof Statements.Data) {
324
324
  found = new data_1.Data().runSyntax(node, input);
@@ -188,7 +188,7 @@ class CDSLexer {
188
188
  break;
189
189
  case "@":
190
190
  // @ starts a new annotation; flush current token and start building with @
191
- build = result.add(build, row, col, mode);
191
+ result.add(build, row, col, mode);
192
192
  build = "@";
193
193
  break;
194
194
  default:
@@ -50,7 +50,7 @@ class Help {
50
50
  return content;
51
51
  }
52
52
  static dumpABAP(file, reg, textDocument, position) {
53
- let content = "";
53
+ let content;
54
54
  content = `
55
55
  <a href="#_tokens" rel="no-refresh">Tokens</a> |
56
56
  <a href="#_statements" rel="no-refresh">Statements</a> |
@@ -83,7 +83,7 @@ class Help {
83
83
  return `<h3 id="_info">Info Dump</h3><pre>` + text + "</pre>";
84
84
  }
85
85
  static cursorInformation(reg, textDocument, position, file) {
86
- let ret = "";
86
+ let ret;
87
87
  const found = _lsp_utils_1.LSPUtils.findCursor(reg, { textDocument, position });
88
88
  if (found !== undefined) {
89
89
  ret = "Statement: " + this.linkToStatement(found.snode.get()) + "<br>\n" +
@@ -121,7 +121,7 @@ class DataElement extends _abstract_object_1.AbstractObject {
121
121
  }
122
122
  parseType(reg) {
123
123
  const references = [];
124
- let lookup = undefined;
124
+ let lookup;
125
125
  this.parse();
126
126
  if (this.parsedXML === undefined) {
127
127
  lookup = { type: new Types.UnknownType("Data Element " + this.getName() + ", parser error") };
@@ -392,6 +392,7 @@ class Table extends _abstract_object_1.AbstractObject {
392
392
  PRECFIELD: field.PRECFIELD,
393
393
  LENG: field.LENG,
394
394
  INTLEN: field.INTLEN,
395
+ INTTYPE: field.INTTYPE,
395
396
  DATATYPE: field.DATATYPE,
396
397
  DECIMALS: field.DECIMALS,
397
398
  KEYFLAG: field.KEYFLAG,
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.71";
78
+ return "2.120.72";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -54,7 +54,7 @@ class AlignPseudoComments extends _abap_rule_1.ABAPRule {
54
54
  }
55
55
  const col = firstCommentToken.getStart().getCol();
56
56
  if (previousEnd.getCol() < expectedColumn && col !== expectedColumn) {
57
- let fix = undefined;
57
+ let fix;
58
58
  if (col < expectedColumn) {
59
59
  fix = edit_helper_1.EditHelper.insertAt(file, firstCommentToken.getStart(), " ".repeat(expectedColumn - col));
60
60
  }
@@ -138,7 +138,7 @@ ENDINTERFACE.`,
138
138
  if (f.after.getCol() === column) {
139
139
  continue;
140
140
  }
141
- let fix = undefined;
141
+ let fix;
142
142
  if (f.after.getCol() < column) {
143
143
  fix = edit_helper_1.EditHelper.insertAt(file, f.after, " ".repeat(column - f.after.getCol()));
144
144
  }
@@ -142,7 +142,7 @@ ENDCASE.`,
142
142
  if (tuples.length === 1) {
143
143
  return false;
144
144
  }
145
- let chain = "";
145
+ let chain;
146
146
  if (tuples[0].left === tuples[1].left) {
147
147
  chain = tuples[0].left;
148
148
  }
@@ -68,7 +68,7 @@ class CheckDDIC {
68
68
  this.conf = conf;
69
69
  }
70
70
  run(obj) {
71
- let found = undefined;
71
+ let found;
72
72
  if (obj instanceof Objects.DataElement
73
73
  || obj instanceof Objects.Domain
74
74
  || obj instanceof Objects.Table
@@ -59,7 +59,6 @@ ENDCLASS.`,
59
59
  this.conf = conf;
60
60
  }
61
61
  runParsed(file, obj) {
62
- let issues = [];
63
62
  if (this.conf.patternKind === undefined) {
64
63
  this.conf.patternKind = "required";
65
64
  }
@@ -80,8 +79,7 @@ ENDCLASS.`,
80
79
  attributes = attributes.concat(intfDef.attributes);
81
80
  }
82
81
  }
83
- issues = this.checkAttributes(attributes);
84
- return issues;
82
+ return this.checkAttributes(attributes);
85
83
  }
86
84
  checkAttributes(attr) {
87
85
  if (attr === undefined) {
@@ -531,7 +531,7 @@ Make sure to test the downported code, it might not always be completely correct
531
531
  if (found) {
532
532
  return found;
533
533
  }
534
- found = this.outlineGetReferenceSimple(high, lowFile);
534
+ found = this.outlineGetReferenceSimple(high, lowFile, highSyntax);
535
535
  if (found) {
536
536
  return found;
537
537
  }
@@ -1206,7 +1206,17 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
1206
1206
  const fix = edit_helper_1.EditHelper.replaceRange(lowFile, node.getStart(), node.getEnd(), code);
1207
1207
  return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Outline DATA", this.getMetadata().key, this.conf.severity, fix);
1208
1208
  }
1209
- outlineGetReferenceSimple(node, lowFile) {
1209
+ /** the type of a reference to the source: LIKE REF TO it, or REF TO data when its type is generic,
1210
+ * as LIKE REF TO a generically typed field symbol or parameter does not activate */
1211
+ refTypeOf(sourceName, node, lowFile, highSyntax) {
1212
+ var _a;
1213
+ const spag = highSyntax.spaghetti.lookupPosition(node.getFirstToken().getStart(), lowFile.getFilename());
1214
+ if (((_a = spag === null || spag === void 0 ? void 0 : spag.findVariable(sourceName)) === null || _a === void 0 ? void 0 : _a.getType().isGeneric()) === true) {
1215
+ return "TYPE REF TO data";
1216
+ }
1217
+ return "LIKE REF TO " + sourceName;
1218
+ }
1219
+ outlineGetReferenceSimple(node, lowFile, highSyntax) {
1210
1220
  var _a, _b, _c;
1211
1221
  if (!(node.get() instanceof Statements.GetReference)) {
1212
1222
  return undefined;
@@ -1223,7 +1233,8 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
1223
1233
  const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
1224
1234
  const firstToken = target.getFirstToken();
1225
1235
  const lastToken = target.getLastToken();
1226
- const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getStart(), `DATA ${targetName} LIKE REF TO ${source.concatTokens()}.\n${indentation}`);
1236
+ const refType = this.refTypeOf(source.concatTokens(), node, lowFile, highSyntax);
1237
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getStart(), `DATA ${targetName} ${refType}.\n${indentation}`);
1227
1238
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, firstToken.getStart(), lastToken.getEnd(), targetName);
1228
1239
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
1229
1240
  return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Outline DATA", this.getMetadata().key, this.conf.severity, fix);
@@ -1240,7 +1251,7 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
1240
1251
  if (!(((_a = target === null || target === void 0 ? void 0 : target.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.InlineData)) {
1241
1252
  return undefined;
1242
1253
  }
1243
- let type = "";
1254
+ let type;
1244
1255
  const source = node.findFirstExpression(Expressions.Source);
1245
1256
  if (source === undefined) {
1246
1257
  return undefined;
@@ -1330,8 +1341,8 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
1330
1341
  if (!(node.get() instanceof Statements.Raise)) {
1331
1342
  return undefined;
1332
1343
  }
1333
- let id = undefined;
1334
- let number = undefined;
1344
+ let id;
1345
+ let number;
1335
1346
  let startToken = node.findDirectTokenByText("ID");
1336
1347
  if (startToken) {
1337
1348
  const sources = node.findDirectExpressions(Expressions.Source);
@@ -1489,7 +1500,7 @@ ${indentation}${uniqueName2}->if_t100_message~t100key = ${uniqueName1}.\n`;
1489
1500
  || sourceRef.getChildren().length !== 1) {
1490
1501
  return undefined;
1491
1502
  }
1492
- let code = "";
1503
+ let code;
1493
1504
  if (sourceRef.findFirstExpression(Expressions.TableExpression)) {
1494
1505
  const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
1495
1506
  code = `ASSIGN ${sourceRef.concatTokens()} TO FIELD-SYMBOL(<${uniqueName}>).
@@ -1681,7 +1692,7 @@ LOOP AT ${groupTargetName}tab ${groupTarget}.`;
1681
1692
  || !(tableExpression instanceof nodes_1.ExpressionNode)) {
1682
1693
  return undefined;
1683
1694
  }
1684
- let condition = "";
1695
+ let condition;
1685
1696
  if (tableExpression.getChildren().length === 3) {
1686
1697
  const index = tableExpression.findDirectExpression(Expressions.Source);
1687
1698
  if (index === undefined) {
@@ -1865,7 +1876,7 @@ ${indentation}${uniqueName}`;
1865
1876
  return undefined;
1866
1877
  }
1867
1878
  const op = secondChild.getFirstToken();
1868
- let operator = "";
1879
+ let operator;
1869
1880
  switch (op.getStr()) {
1870
1881
  case "+":
1871
1882
  operator = " + ";
@@ -1907,7 +1918,7 @@ ${indentation}${uniqueName}`;
1907
1918
  || (formatting === null || formatting === void 0 ? void 0 : formatting.startsWith("ALPHA = ")) === false) {
1908
1919
  continue;
1909
1920
  }
1910
- let functionName = "";
1921
+ let functionName;
1911
1922
  switch (formatting) {
1912
1923
  case "ALPHA = IN":
1913
1924
  functionName = "CONVERSION_EXIT_ALPHA_INPUT";
@@ -2203,13 +2214,12 @@ ${indentation} output = ${uniqueName}.\n`;
2203
2214
  const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
2204
2215
  const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
2205
2216
  let body = "";
2206
- let name = "";
2207
2217
  const switchBody = i.findDirectExpression(Expressions.SwitchBody);
2208
2218
  if (switchBody === undefined) {
2209
2219
  continue;
2210
2220
  }
2211
2221
  for (const l of ((_c = switchBody === null || switchBody === void 0 ? void 0 : switchBody.findDirectExpression(Expressions.Let)) === null || _c === void 0 ? void 0 : _c.findDirectExpressions(Expressions.InlineFieldDefinition)) || []) {
2212
- name = l.getFirstToken().getStr();
2222
+ const name = l.getFirstToken().getStr();
2213
2223
  body += indentation + `DATA(${name}) = ${(_d = switchBody.findFirstExpression(Expressions.Source)) === null || _d === void 0 ? void 0 : _d.concatTokens()}.\n`;
2214
2224
  }
2215
2225
  body += `DATA ${uniqueName} ${type}.\n`;
@@ -2267,7 +2277,6 @@ ${indentation} output = ${uniqueName}.\n`;
2267
2277
  const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
2268
2278
  const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
2269
2279
  let body = "";
2270
- let name = "";
2271
2280
  const reduceBody = i.findDirectExpression(Expressions.ReduceBody);
2272
2281
  if (reduceBody === undefined) {
2273
2282
  continue;
@@ -2278,7 +2287,7 @@ ${indentation} output = ${uniqueName}.\n`;
2278
2287
  }
2279
2288
  let firstName = "";
2280
2289
  for (const init of reduceBody.findDirectExpressions(Expressions.InlineFieldDefinition)) {
2281
- name = init.getFirstToken().getStr();
2290
+ const name = init.getFirstToken().getStr();
2282
2291
  if (firstName === "") {
2283
2292
  firstName = name;
2284
2293
  }
@@ -2510,7 +2519,7 @@ ${indentation} output = ${uniqueName}.\n`;
2510
2519
  after += c.concatTokens();
2511
2520
  }
2512
2521
  }
2513
- let condition = "";
2522
+ let condition;
2514
2523
  if ((tableExpression === null || tableExpression === void 0 ? void 0 : tableExpression.getChildren().length) === 3) {
2515
2524
  condition = "INDEX " + ((_d = tableExpression === null || tableExpression === void 0 ? void 0 : tableExpression.findDirectExpression(Expressions.Source)) === null || _d === void 0 ? void 0 : _d.concatTokens());
2516
2525
  }
@@ -2661,7 +2670,7 @@ ${indentation} output = ${uniqueName}.\n`;
2661
2670
  continue;
2662
2671
  }
2663
2672
  const name = nameToken.getStr();
2664
- let type = "";
2673
+ let type;
2665
2674
  if (high.concatTokens().toUpperCase().startsWith("APPEND INITIAL LINE TO ")) {
2666
2675
  type = "LIKE LINE OF " + ((_b = high.findFirstExpression(Expressions.Target)) === null || _b === void 0 ? void 0 : _b.concatTokens());
2667
2676
  }
@@ -3027,7 +3036,7 @@ ${indentation} output = ${uniqueName}.\n`;
3027
3036
  }
3028
3037
  return undefined;
3029
3038
  }
3030
- getReference(node, lowFile, _highSyntax) {
3039
+ getReference(node, lowFile, highSyntax) {
3031
3040
  var _a, _b, _c;
3032
3041
  if (!(node.get() instanceof Statements.GetReference)) {
3033
3042
  return undefined;
@@ -3041,7 +3050,7 @@ ${indentation} output = ${uniqueName}.\n`;
3041
3050
  if (targetName === undefined || sourceName === undefined) {
3042
3051
  return undefined;
3043
3052
  }
3044
- const code = `DATA ${targetName} LIKE REF TO ${sourceName}.\n`;
3053
+ const code = `DATA ${targetName} ${this.refTypeOf(sourceName, node, lowFile, highSyntax)}.\n`;
3045
3054
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
3046
3055
  const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inline.getFirstToken().getStart(), inline.getLastToken().getEnd(), targetName);
3047
3056
  const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
@@ -3118,18 +3127,30 @@ ${indentation} output = ${uniqueName}.\n`;
3118
3127
  const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
3119
3128
  const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
3120
3129
  const sy = func === "LINE_EXISTS" ? "sy-subrc" : "sy-tabix";
3121
- const code = `DATA ${uniqueName} LIKE sy-subrc.\n` +
3122
- indentation + `READ TABLE ${tableName} ${condition}TRANSPORTING NO FIELDS.\n` +
3123
- indentation + uniqueName + ` = ${sy}.\n` +
3124
- indentation;
3125
3130
  let insertAt = node.getFirstToken().getStart();
3126
3131
  if (node.get() instanceof statements_1.ElseIf) {
3127
- // assumption: no side effects in IF conditions
3132
+ // assumption: no side effects in IF conditions, except the READ itself, see below
3128
3133
  insertAt = this.findStartOfIf(node, highFile);
3129
3134
  if (insertAt === undefined) {
3130
3135
  continue;
3131
3136
  }
3132
3137
  }
3138
+ // the READ sets sy-subrc and sy-tabix, which the built-in function does not,
3139
+ // keep the values for the code that reads them after the inserted READ
3140
+ const keep = [];
3141
+ const reads = this.readSyFields(insertAt, highFile);
3142
+ for (const field of ["sy-subrc", "sy-tabix"]) {
3143
+ if (reads.has(field)) {
3144
+ keep.push({ field, name: this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax) });
3145
+ }
3146
+ }
3147
+ const code = `DATA ${uniqueName} LIKE sy-subrc.\n` +
3148
+ keep.map(k => indentation + `DATA ${k.name} LIKE ${k.field}.\n`).join("") +
3149
+ keep.map(k => indentation + `${k.name} = ${k.field}.\n`).join("") +
3150
+ indentation + `READ TABLE ${tableName} ${condition}TRANSPORTING NO FIELDS.\n` +
3151
+ indentation + uniqueName + ` = ${sy}.\n` +
3152
+ keep.map(k => indentation + `${k.field} = ${k.name}.\n`).join("") +
3153
+ indentation;
3133
3154
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, insertAt, code);
3134
3155
  const start = expression.getFirstToken().getStart();
3135
3156
  const end = expression.getLastToken().getEnd();
@@ -3140,6 +3161,46 @@ ${indentation} output = ${uniqueName}.\n`;
3140
3161
  }
3141
3162
  return undefined;
3142
3163
  }
3164
+ /** sy-subrc and sy-tabix, when read from the position on, in the procedure around it or the file outside of one;
3165
+ * inside a loop from the start of the outermost loop, as the next pass reads what this one left */
3166
+ readSyFields(position, highFile) {
3167
+ const structure = highFile.getStructure();
3168
+ const contains = (s) => !position.isBefore(s.getFirstToken().getStart())
3169
+ && !position.isAfter(s.getLastToken().getStart());
3170
+ let scope = undefined;
3171
+ for (const s of [Structures.Method, Structures.Form, Structures.FunctionModule]) {
3172
+ for (const p of (structure === null || structure === void 0 ? void 0 : structure.findAllStructuresRecursive(s)) || []) {
3173
+ if (contains(p)) {
3174
+ scope = p;
3175
+ }
3176
+ }
3177
+ }
3178
+ scope = scope || structure;
3179
+ let from = position;
3180
+ for (const s of [Structures.Loop, Structures.Do, Structures.While, Structures.Select]) {
3181
+ for (const l of (scope === null || scope === void 0 ? void 0 : scope.findAllStructuresRecursive(s)) || []) {
3182
+ if (contains(l) && l.getFirstToken().getStart().isBefore(from)) {
3183
+ from = l.getFirstToken().getStart();
3184
+ }
3185
+ }
3186
+ }
3187
+ const ret = new Set();
3188
+ for (const s of (scope === null || scope === void 0 ? void 0 : scope.findAllStatementNodes()) || []) {
3189
+ if (s.getLastToken().getStart().isBefore(from)) {
3190
+ continue;
3191
+ }
3192
+ const tokens = s.getTokens();
3193
+ for (let i = 0; i < tokens.length - 2; i++) {
3194
+ if (tokens[i].getStr().toUpperCase() === "SY" && tokens[i + 1].getStr() === "-") {
3195
+ const field = "sy-" + tokens[i + 2].getStr().toLowerCase();
3196
+ if (field === "sy-subrc" || field === "sy-tabix") {
3197
+ ret.add(field);
3198
+ }
3199
+ }
3200
+ }
3201
+ }
3202
+ return ret;
3203
+ }
3143
3204
  findStartOfIf(node, highFile) {
3144
3205
  const structure = highFile.getStructure();
3145
3206
  for (const c of (structure === null || structure === void 0 ? void 0 : structure.findAllStructuresRecursive(Structures.If)) || []) {
@@ -3152,6 +3213,7 @@ ${indentation} output = ${uniqueName}.\n`;
3152
3213
  return undefined;
3153
3214
  }
3154
3215
  newToCreateObject(low, high, lowFile, highSyntax) {
3216
+ var _a;
3155
3217
  if (!(low.get() instanceof _statement_1.Unknown)) {
3156
3218
  return undefined;
3157
3219
  }
@@ -3167,7 +3229,7 @@ ${indentation} output = ${uniqueName}.\n`;
3167
3229
  && found
3168
3230
  && source.concatTokens() === found.concatTokens()
3169
3231
  && target.findDirectExpression(Expressions.InlineData) === undefined) {
3170
- const abap = this.newParameters(found, target.concatTokens(), highSyntax, lowFile);
3232
+ const abap = (_a = this.newDataReference(high, target, found, lowFile, highSyntax)) !== null && _a !== void 0 ? _a : this.newParameters(found, target.concatTokens(), highSyntax, lowFile);
3171
3233
  if (abap !== undefined) {
3172
3234
  fix = edit_helper_1.EditHelper.replaceRange(lowFile, high.getFirstToken().getStart(), high.getLastToken().getEnd(), abap);
3173
3235
  }
@@ -3206,6 +3268,42 @@ ${indentation} output = ${uniqueName}.\n`;
3206
3268
  return undefined;
3207
3269
  }
3208
3270
  }
3271
+ /** NEW into a data reference creates data, CREATE OBJECT cannot: "target = NEW #|type( [value] )"
3272
+ * with a variable typed REF TO a data type becomes CREATE DATA plus the assignment of the value */
3273
+ newDataReference(high, target, found, lowFile, highSyntax) {
3274
+ var _a, _b, _c;
3275
+ if (target.getChildren().length !== 1 || !(((_a = target.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.TargetField)) {
3276
+ return undefined;
3277
+ }
3278
+ const children = found.getChildren();
3279
+ const value = found.findDirectExpression(Expressions.Source);
3280
+ // NEW, the type, "(", optionally one value, ")"
3281
+ if (children.length !== 4 + (value ? 1 : 0)) {
3282
+ return undefined;
3283
+ }
3284
+ const name = target.concatTokens();
3285
+ const spag = highSyntax.spaghetti.lookupPosition(high.getFirstToken().getStart(), lowFile.getFilename());
3286
+ const type = (_b = spag === null || spag === void 0 ? void 0 : spag.findVariable(name)) === null || _b === void 0 ? void 0 : _b.getType();
3287
+ if (!(type instanceof basic_1.DataReference)) {
3288
+ return undefined;
3289
+ }
3290
+ const typeName = (_c = found.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _c === void 0 ? void 0 : _c.concatTokens();
3291
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
3292
+ let abap = `CREATE DATA ${name}` + (typeName === "#" ? "" : ` TYPE ${typeName}`) + ".";
3293
+ if (value === undefined) {
3294
+ return abap;
3295
+ }
3296
+ else if (type.getType().isGeneric() === false) {
3297
+ return abap + `\n${indentation}${name}->* = ${value.concatTokens()}.`;
3298
+ }
3299
+ // a generic reference cannot be dereferenced as a target below v756
3300
+ const fs = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax, true);
3301
+ abap = `FIELD-SYMBOLS ${fs} TYPE any.\n` +
3302
+ indentation + abap + "\n" +
3303
+ indentation + `ASSIGN ${name}->* TO ${fs}.\n` +
3304
+ indentation + `${fs} = ${value.concatTokens()}.`;
3305
+ return abap;
3306
+ }
3209
3307
  newParameters(found, name, highSyntax, lowFile) {
3210
3308
  var _a, _b, _c;
3211
3309
  const typeToken = (_a = found.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
@@ -63,6 +63,8 @@ class EmptyStructureConf extends _basic_rule_config_1.BasicRuleConfig {
63
63
  this.try = true;
64
64
  /** Checks for empty WHEN blocks */
65
65
  this.when = true;
66
+ /** Checks for empty CATCH blocks, unless the CATCH has pragma ##NO_HANDLER or pseudo comment "#EC NO_HANDLER */
67
+ this.catch = false;
66
68
  // todo, other category containing ELSE
67
69
  }
68
70
  }
@@ -175,6 +177,21 @@ result = xsdbool( sy-subrc = 0 ).`,
175
177
  }
176
178
  }
177
179
  }
180
+ if (this.getConfig().catch === true) {
181
+ for (const c of stru.findAllStructuresRecursive(Structures.Catch)) {
182
+ // a comment in the block does not count, the extended check (SLIN) reports it too
183
+ if (c.findDirectStructure(Structures.Body) !== undefined) {
184
+ continue;
185
+ }
186
+ const catchStatement = c.getFirstStatement();
187
+ if (catchStatement === undefined || this.isNoHandler(catchStatement, statements)) {
188
+ continue;
189
+ }
190
+ const token = c.getFirstToken();
191
+ const issue = issue_1.Issue.atToken(file, token, this.getDescription(c.get().constructor.name), this.getMetadata().key, this.conf.severity);
192
+ issues.push(issue);
193
+ }
194
+ }
178
195
  if (this.getConfig().when === true) {
179
196
  const tries = stru.findAllStructuresRecursive(Structures.When);
180
197
  for (const t of tries) {
@@ -188,6 +205,15 @@ result = xsdbool( sy-subrc = 0 ).`,
188
205
  }
189
206
  return issues;
190
207
  }
208
+ isNoHandler(catchStatement, statements) {
209
+ if (catchStatement.getPragmas().some(p => p.getStr().toUpperCase() === "##NO_HANDLER")) {
210
+ return true;
211
+ }
212
+ const next = statements[statements.indexOf(catchStatement) + 1];
213
+ return (next === null || next === void 0 ? void 0 : next.get()) instanceof _statement_1.Comment
214
+ && next.getStart().getRow() === catchStatement.getEnd().getRow()
215
+ && next.concatTokens().toUpperCase().replace(/\s/g, "").includes("\"#ECNO_HANDLER");
216
+ }
191
217
  }
192
218
  exports.EmptyStructure = EmptyStructure;
193
219
  //# sourceMappingURL=empty_structure.js.map
@@ -138,7 +138,7 @@ cl_abap_typedescr=>describe_by_name(
138
138
  const methodSource = statNode.findDirectExpression(Expressions.MethodSource);
139
139
  let methodSourceStr = methodSource === null || methodSource === void 0 ? void 0 : methodSource.concatTokens();
140
140
  const methodBody = statNode.findDirectExpression(Expressions.MethodCallBody);
141
- let methodBodyStr = "";
141
+ let methodBodyStr;
142
142
  if (methodBody) {
143
143
  const methodCallParam = methodBody.findDirectExpression(Expressions.MethodCallParam);
144
144
  if (methodCallParam && methodCallParam.getFirstToken().getStr() === "(") {
@@ -215,7 +215,7 @@ class KeywordCase extends _abap_rule_1.ABAPRule {
215
215
  const firstToken = tokens[0].token;
216
216
  const lastToken = tokens[tokens.length - 1].token;
217
217
  const firstTokenValue = firstToken.getStr();
218
- let description = "";
218
+ let description;
219
219
  if (first.keyword === true) {
220
220
  description = `Keyword should be ${this.conf.style} case: "${firstTokenValue}"`;
221
221
  }
@@ -58,7 +58,7 @@ ENDCLASS.`,
58
58
  continue;
59
59
  }
60
60
  const className = classDef.name;
61
- let expected = "";
61
+ let expected;
62
62
  if (classDef.isForTesting) {
63
63
  expected = this.conf.test;
64
64
  }
@@ -86,7 +86,7 @@ ENDIF.
86
86
  issues.push(...this.analyzeCondition(file, cond));
87
87
  }
88
88
  for (const sub of structure.findAllExpressionsMulti([Expressions.CondSub, Expressions.ComponentCondSub])) {
89
- let cond = [];
89
+ let cond;
90
90
  if (sub.get() instanceof Expressions.CondSub) {
91
91
  cond = sub.findDirectExpressions(Expressions.Cond);
92
92
  }