@abaplint/core 2.85.22 → 2.85.25
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/src/abap/2_statements/expressions/sql_as_name.js +3 -1
- package/build/src/abap/2_statements/expressions/sql_field_list.js +1 -1
- package/build/src/abap/5_syntax/expressions/method_source.js +6 -2
- package/build/src/abap/5_syntax/expressions/source.js +5 -2
- package/build/src/abap/5_syntax/expressions/sql_for_all_entries.js +1 -1
- package/build/src/abap/5_syntax/expressions/switch_body.js +9 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/downport.js +141 -11
- package/package.json +3 -3
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SQLAsName = void 0;
|
|
4
|
+
const tokens_1 = require("../../1_lexer/tokens");
|
|
4
5
|
const combi_1 = require("../combi");
|
|
5
6
|
class SQLAsName extends combi_1.Expression {
|
|
6
7
|
getRunnable() {
|
|
7
8
|
// todo, below allows too much?
|
|
8
|
-
|
|
9
|
+
const field = (0, combi_1.regex)(/^[&_!]?\*?\w*(\/\w+\/)?\d*[a-zA-Z_%\$][\w\*%\$\?]*(~\w+)?$/);
|
|
10
|
+
return (0, combi_1.seq)(field, (0, combi_1.starPrio)((0, combi_1.seq)((0, combi_1.tok)(tokens_1.Dash), field)));
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
exports.SQLAsName = SQLAsName;
|
|
@@ -12,7 +12,7 @@ class SQLFieldList extends combi_1.Expression {
|
|
|
12
12
|
getRunnable() {
|
|
13
13
|
const comma = (0, combi_1.optPrio)((0, combi_1.ver)(version_1.Version.v740sp05, ","));
|
|
14
14
|
const abap = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WAt), simple_field_chain_1.SimpleFieldChain));
|
|
15
|
-
const as = (0, combi_1.seq)("AS", _1.
|
|
15
|
+
const as = (0, combi_1.seq)("AS", _1.SQLAsName);
|
|
16
16
|
const field = (0, combi_1.altPrio)(_1.SQLAggregation, _1.SQLCase, sql_function_1.SQLFunction, sql_path_1.SQLPath, _1.SQLFieldName, abap, _1.Constant);
|
|
17
17
|
const sub = (0, combi_1.plusPrio)((0, combi_1.seq)((0, combi_1.altPrio)("+", "-", "*", "/", "&&"), (0, combi_1.optPrio)((0, combi_1.tok)(tokens_1.WParenLeftW)), field, (0, combi_1.optPrio)((0, combi_1.tok)(tokens_1.WParenRightW))));
|
|
18
18
|
const arith = (0, combi_1.ver)(version_1.Version.v740sp05, sub);
|
|
@@ -11,6 +11,7 @@ const _reference_1 = require("../_reference");
|
|
|
11
11
|
const _object_oriented_1 = require("../_object_oriented");
|
|
12
12
|
class MethodSource {
|
|
13
13
|
runSyntax(node, scope, filename) {
|
|
14
|
+
// todo, rewrite the context finding, and/or restructure the expression?
|
|
14
15
|
const context = new method_call_chain_1.MethodCallChain().runSyntax(node, scope, filename);
|
|
15
16
|
const last = node.getLastChild();
|
|
16
17
|
const first = node.getFirstChild();
|
|
@@ -19,8 +20,11 @@ class MethodSource {
|
|
|
19
20
|
}
|
|
20
21
|
else if (last instanceof nodes_1.ExpressionNode && last.get() instanceof Expressions.MethodName) {
|
|
21
22
|
if (context instanceof basic_1.ObjectReferenceType) {
|
|
22
|
-
|
|
23
|
-
if (id instanceof types_1.ClassDefinition) {
|
|
23
|
+
let id = context.getIdentifier();
|
|
24
|
+
if (!(id instanceof types_1.ClassDefinition)) {
|
|
25
|
+
id = scope.findObjectDefinition(id.getName());
|
|
26
|
+
}
|
|
27
|
+
if (id instanceof types_1.ClassDefinition) { // todo || id instanceof InterfaceDefinition) {
|
|
24
28
|
const methodName = last.concatTokens().toUpperCase();
|
|
25
29
|
const helper = new _object_oriented_1.ObjectOriented(scope);
|
|
26
30
|
const { method: foundMethod } = helper.searchMethodName(id, methodName);
|
|
@@ -72,8 +72,11 @@ class Source {
|
|
|
72
72
|
case "SWITCH":
|
|
73
73
|
{
|
|
74
74
|
const foundType = this.determineType(node, scope, filename, targetType);
|
|
75
|
-
new switch_body_1.SwitchBody().runSyntax(node.findDirectExpression(Expressions.SwitchBody), scope, filename);
|
|
76
|
-
|
|
75
|
+
const bodyType = new switch_body_1.SwitchBody().runSyntax(node.findDirectExpression(Expressions.SwitchBody), scope, filename);
|
|
76
|
+
if (foundType === undefined) {
|
|
77
|
+
this.addIfInferred(node, scope, filename, bodyType);
|
|
78
|
+
}
|
|
79
|
+
return foundType ? foundType : bodyType;
|
|
77
80
|
}
|
|
78
81
|
case "COND":
|
|
79
82
|
{
|
|
@@ -18,7 +18,7 @@ class SQLForAllEntries {
|
|
|
18
18
|
if (!(type instanceof basic_1.TableType)) {
|
|
19
19
|
throw new Error("FAE parameter must be table type");
|
|
20
20
|
}
|
|
21
|
-
const name = s.concatTokens();
|
|
21
|
+
const name = s.concatTokens().replace("[]", "");
|
|
22
22
|
scope.setAllowHeaderUse(name);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -8,9 +8,18 @@ class SwitchBody {
|
|
|
8
8
|
if (node === undefined) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
+
const thenSource = node.findExpressionAfterToken("THEN");
|
|
12
|
+
if (!((thenSource === null || thenSource === void 0 ? void 0 : thenSource.get()) instanceof Expressions.Source)) {
|
|
13
|
+
throw new Error("SwitchBody, unexpected");
|
|
14
|
+
}
|
|
15
|
+
const type = new source_1.Source().runSyntax(thenSource, scope, filename);
|
|
11
16
|
for (const s of node.findDirectExpressions(Expressions.Source)) {
|
|
17
|
+
if (s === thenSource) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
12
20
|
new source_1.Source().runSyntax(s, scope, filename);
|
|
13
21
|
}
|
|
22
|
+
return type;
|
|
14
23
|
}
|
|
15
24
|
}
|
|
16
25
|
exports.SwitchBody = SwitchBody;
|
package/build/src/registry.js
CHANGED
|
@@ -44,19 +44,21 @@ Current rules:
|
|
|
44
44
|
* CONV is outlined
|
|
45
45
|
* COND is outlined
|
|
46
46
|
* REDUCE is outlined
|
|
47
|
+
* SWITCH is outlined
|
|
48
|
+
* APPEND expression is outlined
|
|
47
49
|
* EMPTY KEY is changed to DEFAULT KEY, opposite of DEFAULT KEY in https://rules.abaplint.org/avoid_use/
|
|
48
50
|
* CAST changed to ?=
|
|
49
51
|
* LOOP AT method_call( ) is outlined
|
|
50
52
|
* VALUE # with structure fields
|
|
51
53
|
* VALUE # with internal table lines
|
|
52
|
-
* Table Expressions
|
|
54
|
+
* Table Expressions are outlined
|
|
53
55
|
* SELECT INTO @DATA definitions are outlined
|
|
54
56
|
* Some occurrences of string template formatting option ALPHA changed to function module call
|
|
55
57
|
* SELECT/INSERT/MODIFY/DELETE/UPDATE "," in field list removed, "@" in source/targets removed
|
|
56
58
|
* PARTIALLY IMPLEMENTED removed, it can be quick fixed via rule implement_methods
|
|
57
59
|
* RAISE EXCEPTION ... MESSAGE
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* Moving with +=, -=, /=, *=, &&= is expanded
|
|
61
|
+
* line_exists and line_index is downported to READ TABLE
|
|
60
62
|
|
|
61
63
|
Only one transformation is applied to a statement at a time, so multiple steps might be required to do the full downport.`,
|
|
62
64
|
tags: [_irule_1.RuleTag.Experimental, _irule_1.RuleTag.Downport, _irule_1.RuleTag.Quickfix],
|
|
@@ -192,6 +194,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
192
194
|
if (found) {
|
|
193
195
|
return found;
|
|
194
196
|
}
|
|
197
|
+
found = this.outlineSwitch(high, lowFile, highSyntax);
|
|
198
|
+
if (found) {
|
|
199
|
+
return found;
|
|
200
|
+
}
|
|
195
201
|
found = this.outlineCast(high, lowFile, highSyntax);
|
|
196
202
|
if (found) {
|
|
197
203
|
return found;
|
|
@@ -224,7 +230,11 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
224
230
|
if (found) {
|
|
225
231
|
return found;
|
|
226
232
|
}
|
|
227
|
-
|
|
233
|
+
found = this.replaceLineFunctions(high, lowFile, highSyntax);
|
|
234
|
+
if (found) {
|
|
235
|
+
return found;
|
|
236
|
+
}
|
|
237
|
+
// note: line_exists() must be replaced before this call
|
|
228
238
|
found = this.replaceTableExpression(high, lowFile, highSyntax);
|
|
229
239
|
if (found) {
|
|
230
240
|
return found;
|
|
@@ -233,7 +243,6 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
233
243
|
if (found) {
|
|
234
244
|
return found;
|
|
235
245
|
}
|
|
236
|
-
// todo, add more rules here
|
|
237
246
|
return undefined;
|
|
238
247
|
}
|
|
239
248
|
//////////////////////////////////////////
|
|
@@ -405,16 +414,11 @@ ${indentation}${uniqueName} = ${source.concatTokens()}.\n${indentation}`);
|
|
|
405
414
|
return undefined;
|
|
406
415
|
}
|
|
407
416
|
replaceTableExpression(node, lowFile, highSyntax) {
|
|
408
|
-
var _a;
|
|
409
417
|
for (const fieldChain of node.findAllExpressionsRecursive(Expressions.FieldChain)) {
|
|
410
418
|
const tableExpression = fieldChain.findDirectExpression(Expressions.TableExpression);
|
|
411
419
|
if (tableExpression === undefined) {
|
|
412
420
|
continue;
|
|
413
421
|
}
|
|
414
|
-
if (tableExpression.getChildren().length > 3) {
|
|
415
|
-
// for now, only support the INDEX scenario
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
422
|
let pre = "";
|
|
419
423
|
let startToken = undefined;
|
|
420
424
|
for (const child of fieldChain.getChildren()) {
|
|
@@ -429,11 +433,24 @@ ${indentation}${uniqueName} = ${source.concatTokens()}.\n${indentation}`);
|
|
|
429
433
|
if (startToken === undefined) {
|
|
430
434
|
continue;
|
|
431
435
|
}
|
|
436
|
+
let condition = "";
|
|
437
|
+
for (const c of tableExpression.getChildren() || []) {
|
|
438
|
+
if (c.getFirstToken().getStr() === "[" || c.getFirstToken().getStr() === "]") {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
else if (c.get() instanceof Expressions.ComponentChainSimple && condition === "") {
|
|
442
|
+
condition = "WITH KEY ";
|
|
443
|
+
}
|
|
444
|
+
else if (c.get() instanceof Expressions.Source && condition === "") {
|
|
445
|
+
condition = "INDEX ";
|
|
446
|
+
}
|
|
447
|
+
condition += c.concatTokens() + " ";
|
|
448
|
+
}
|
|
432
449
|
const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
433
450
|
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
434
451
|
const firstToken = node.getFirstToken();
|
|
435
452
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
|
|
436
|
-
${indentation}READ TABLE ${pre}
|
|
453
|
+
${indentation}READ TABLE ${pre} ${condition}INTO ${uniqueName}.
|
|
437
454
|
${indentation}IF sy-subrc <> 0.
|
|
438
455
|
${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
|
|
439
456
|
${indentation}ENDIF.
|
|
@@ -732,6 +749,67 @@ ${indentation} output = ${topTarget}.`;
|
|
|
732
749
|
}
|
|
733
750
|
return { body, end };
|
|
734
751
|
}
|
|
752
|
+
outlineSwitch(node, lowFile, highSyntax) {
|
|
753
|
+
var _a, _b;
|
|
754
|
+
for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
|
|
755
|
+
const firstToken = i.getFirstToken();
|
|
756
|
+
if (firstToken.getStr().toUpperCase() !== "SWITCH") {
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
const type = this.findType(i, lowFile, highSyntax);
|
|
760
|
+
if (type === undefined) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
|
|
764
|
+
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
765
|
+
let body = "";
|
|
766
|
+
let name = "";
|
|
767
|
+
const switchBody = i.findDirectExpression(Expressions.SwitchBody);
|
|
768
|
+
if (switchBody === undefined) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
for (const l of ((_a = switchBody === null || switchBody === void 0 ? void 0 : switchBody.findDirectExpression(Expressions.Let)) === null || _a === void 0 ? void 0 : _a.findDirectExpressions(Expressions.InlineFieldDefinition)) || []) {
|
|
772
|
+
name = l.getFirstToken().getStr();
|
|
773
|
+
body += indentation + `DATA(${name}) = ${(_b = switchBody.findFirstExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.concatTokens()}.\n`;
|
|
774
|
+
}
|
|
775
|
+
body += `DATA ${uniqueName} TYPE ${type}.\n`;
|
|
776
|
+
let firstSource = false;
|
|
777
|
+
let inWhen = false;
|
|
778
|
+
for (const c of switchBody.getChildren()) {
|
|
779
|
+
if (c.get() instanceof Expressions.Source && firstSource === false) {
|
|
780
|
+
body += indentation + `CASE ${c.concatTokens()}.`;
|
|
781
|
+
firstSource = true;
|
|
782
|
+
}
|
|
783
|
+
else if (c instanceof nodes_1.TokenNode && c.concatTokens().toUpperCase() === "THEN") {
|
|
784
|
+
inWhen = true;
|
|
785
|
+
body += ".\n";
|
|
786
|
+
}
|
|
787
|
+
else if (c instanceof nodes_1.TokenNode && c.concatTokens().toUpperCase() === "WHEN") {
|
|
788
|
+
inWhen = false;
|
|
789
|
+
body += `\n${indentation} WHEN `;
|
|
790
|
+
}
|
|
791
|
+
else if (c instanceof nodes_1.TokenNode && c.concatTokens().toUpperCase() === "OR") {
|
|
792
|
+
body += ` OR `;
|
|
793
|
+
}
|
|
794
|
+
else if (c instanceof nodes_1.TokenNode && c.concatTokens().toUpperCase() === "ELSE") {
|
|
795
|
+
inWhen = true;
|
|
796
|
+
body += `\n${indentation} WHEN OTHERS.\n`;
|
|
797
|
+
}
|
|
798
|
+
else if (inWhen === false) {
|
|
799
|
+
body += c.concatTokens();
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
body += indentation + " " + uniqueName + " = " + c.concatTokens() + ".";
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
body += "\n" + indentation + "ENDCASE.\n" + indentation;
|
|
806
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), body);
|
|
807
|
+
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, firstToken.getStart(), i.getLastToken().getEnd(), uniqueName);
|
|
808
|
+
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
809
|
+
return issue_1.Issue.atToken(lowFile, firstToken, "Downport SWITCH", this.getMetadata().key, this.conf.severity, fix);
|
|
810
|
+
}
|
|
811
|
+
return undefined;
|
|
812
|
+
}
|
|
735
813
|
outlineReduce(node, lowFile, highSyntax) {
|
|
736
814
|
var _a;
|
|
737
815
|
for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
|
|
@@ -1089,6 +1167,58 @@ ${indentation} output = ${topTarget}.`;
|
|
|
1089
1167
|
}
|
|
1090
1168
|
return undefined;
|
|
1091
1169
|
}
|
|
1170
|
+
findMethodCallExpression(node, token) {
|
|
1171
|
+
var _a;
|
|
1172
|
+
for (const m of node.findAllExpressions(Expressions.MethodCall)) {
|
|
1173
|
+
if ((_a = m.findDirectExpression(Expressions.MethodName)) === null || _a === void 0 ? void 0 : _a.getFirstToken().getStart().equals(token.getStart())) {
|
|
1174
|
+
return m;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
replaceLineFunctions(node, lowFile, highSyntax) {
|
|
1180
|
+
var _a, _b;
|
|
1181
|
+
const spag = highSyntax.spaghetti.lookupPosition(node.getFirstToken().getStart(), lowFile.getFilename());
|
|
1182
|
+
for (const r of (spag === null || spag === void 0 ? void 0 : spag.getData().references) || []) {
|
|
1183
|
+
const func = r.position.getName().toUpperCase();
|
|
1184
|
+
if (r.referenceType === _reference_1.ReferenceType.BuiltinMethodReference
|
|
1185
|
+
&& (func === "LINE_EXISTS" || func === "LINE_INDEX")) {
|
|
1186
|
+
const token = r.position.getToken();
|
|
1187
|
+
const expression = this.findMethodCallExpression(node, token);
|
|
1188
|
+
if (expression === undefined) {
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
let condition = "";
|
|
1192
|
+
for (const c of ((_a = expression === null || expression === void 0 ? void 0 : expression.findFirstExpression(Expressions.TableExpression)) === null || _a === void 0 ? void 0 : _a.getChildren()) || []) {
|
|
1193
|
+
if (c.getFirstToken().getStr() === "[" || c.getFirstToken().getStr() === "]") {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
else if (c.get() instanceof Expressions.ComponentChainSimple && condition === "") {
|
|
1197
|
+
condition = "WITH KEY ";
|
|
1198
|
+
}
|
|
1199
|
+
else if (c.get() instanceof Expressions.Source && condition === "") {
|
|
1200
|
+
condition = "INDEX ";
|
|
1201
|
+
}
|
|
1202
|
+
condition += c.concatTokens() + " ";
|
|
1203
|
+
}
|
|
1204
|
+
const tableName = (_b = expression === null || expression === void 0 ? void 0 : expression.findFirstExpression(Expressions.SourceField)) === null || _b === void 0 ? void 0 : _b.concatTokens();
|
|
1205
|
+
const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
1206
|
+
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
1207
|
+
const sy = func === "LINE_EXISTS" ? "sy-subrc" : "sy-tabix";
|
|
1208
|
+
const code = `DATA ${uniqueName} LIKE sy-subrc.\n` +
|
|
1209
|
+
indentation + `READ TABLE ${tableName} ${condition}TRANSPORTING NO FIELDS.\n` +
|
|
1210
|
+
indentation + uniqueName + ` = ${sy}.\n` +
|
|
1211
|
+
indentation;
|
|
1212
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
|
|
1213
|
+
const start = expression.getFirstToken().getStart();
|
|
1214
|
+
const end = expression.getLastToken().getEnd();
|
|
1215
|
+
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, start, end, uniqueName + (func === "LINE_EXISTS" ? " = 0" : ""));
|
|
1216
|
+
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
1217
|
+
return issue_1.Issue.atToken(lowFile, token, "Use BOOLC", this.getMetadata().key, this.conf.severity, fix);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return undefined;
|
|
1221
|
+
}
|
|
1092
1222
|
newToCreateObject(node, lowFile, highSyntax) {
|
|
1093
1223
|
const source = node.findDirectExpression(Expressions.Source);
|
|
1094
1224
|
let fix = undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.85.
|
|
3
|
+
"version": "2.85.25",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"@microsoft/api-extractor": "^7.19.4",
|
|
49
49
|
"@types/chai": "^4.3.0",
|
|
50
50
|
"@types/mocha": "^9.1.0",
|
|
51
|
-
"@types/node": "^17.0.
|
|
51
|
+
"@types/node": "^17.0.21",
|
|
52
52
|
"chai": "^4.3.6",
|
|
53
|
-
"eslint": "^8.
|
|
53
|
+
"eslint": "^8.10.0",
|
|
54
54
|
"mocha": "^9.2.1",
|
|
55
55
|
"c8": "^7.11.0",
|
|
56
56
|
"source-map-support": "^0.5.21",
|