@abaplint/core 2.120.71 → 2.120.73
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.
- package/build/abaplint.d.ts +10 -0
- package/build/src/abap/1_lexer/lexer.js +1 -1
- package/build/src/abap/1_lexer/lexer_stream.js +8 -0
- package/build/src/abap/3_structures/structures/_combi.js +61 -112
- package/build/src/abap/5_syntax/basic_types.js +4 -4
- package/build/src/abap/5_syntax/expressions/_check_database_work_area.js +127 -0
- package/build/src/abap/5_syntax/expressions/attribute_name.js +1 -1
- package/build/src/abap/5_syntax/expressions/conv_body.js +2 -2
- package/build/src/abap/5_syntax/expressions/field_chain.js +1 -1
- package/build/src/abap/5_syntax/expressions/form_param.js +1 -1
- package/build/src/abap/5_syntax/expressions/method_parameters.js +2 -2
- package/build/src/abap/5_syntax/expressions/select.js +1 -2
- package/build/src/abap/5_syntax/expressions/source.js +19 -3
- package/build/src/abap/5_syntax/statements/assign.js +15 -1
- package/build/src/abap/5_syntax/statements/insert_database.js +6 -3
- package/build/src/abap/5_syntax/statements/loop.js +1 -1
- package/build/src/abap/5_syntax/statements/modify_database.js +8 -4
- package/build/src/abap/5_syntax/statements/update_database.js +7 -3
- package/build/src/abap/5_syntax/statements/when_type.js +1 -1
- package/build/src/abap/types/class_attributes.js +1 -1
- package/build/src/cds/cds_lexer.js +1 -1
- package/build/src/lsp/help.js +2 -2
- package/build/src/objects/data_element.js +1 -1
- package/build/src/objects/table.js +1 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/align_pseudo_comments.js +1 -1
- package/build/src/rules/align_type_expressions.js +1 -1
- package/build/src/rules/change_if_to_case.js +1 -1
- package/build/src/rules/check_ddic.js +1 -1
- package/build/src/rules/class_attribute_names.js +1 -3
- package/build/src/rules/downport.js +122 -24
- package/build/src/rules/empty_structure.js +26 -0
- package/build/src/rules/functional_writing.js +1 -1
- package/build/src/rules/generic_regex.js +86 -0
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/keyword_case.js +1 -1
- package/build/src/rules/local_class_naming.js +1 -1
- package/build/src/rules/many_parentheses.js +1 -1
- package/build/src/rules/obsolete_statement.js +3 -7
- package/build/src/rules/parser_missing_space.js +1 -2
- package/build/src/rules/prefer_raise_exception_new.js +2 -8
- package/build/src/rules/xml_consistency.js +16 -0
- package/build/src/utils/form_length_stats.js +1 -1
- package/build/src/utils/method_length_stats.js +1 -1
- 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
|
|
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
|
-
|
|
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
|
|
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) {
|
package/build/src/lsp/help.js
CHANGED
|
@@ -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
|
|
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") };
|
package/build/src/registry.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
1334
|
-
let number
|
|
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,
|
|
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}
|
|
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() === "(") {
|