@abaplint/core 2.85.43 → 2.85.46
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/5_syntax/_type_utils.js +27 -0
- package/build/src/abap/5_syntax/statements/split.js +17 -2
- package/build/src/registry.js +1 -1
- package/build/src/rules/downport.js +82 -0
- package/build/src/rules/method_overwrites_builtin.js +4 -2
- package/build/src/rules/pragma_placement.js +2 -1
- package/package.json +8 -8
|
@@ -7,6 +7,33 @@ class TypeUtils {
|
|
|
7
7
|
constructor(scope) {
|
|
8
8
|
this.scope = scope;
|
|
9
9
|
}
|
|
10
|
+
isCharLikeStrict(type) {
|
|
11
|
+
if (type === undefined) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
else if (type instanceof basic_1.StructureType) {
|
|
15
|
+
for (const c of type.getComponents()) {
|
|
16
|
+
if (this.isCharLikeStrict(c.type) === false) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
else if (type instanceof basic_1.StringType
|
|
23
|
+
|| type instanceof basic_1.AnyType
|
|
24
|
+
|| type instanceof basic_1.CharacterType
|
|
25
|
+
|| type instanceof basic_1.CLikeType
|
|
26
|
+
|| type instanceof basic_1.DateType
|
|
27
|
+
|| type instanceof basic_1.CSequenceType
|
|
28
|
+
|| type instanceof basic_1.NumericGenericType
|
|
29
|
+
|| type instanceof basic_1.NumericType
|
|
30
|
+
|| type instanceof basic_1.TimeType
|
|
31
|
+
|| type instanceof basic_1.UnknownType
|
|
32
|
+
|| type instanceof basic_1.VoidType) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
10
37
|
isCharLike(type) {
|
|
11
38
|
if (type === undefined) {
|
|
12
39
|
return false;
|
|
@@ -6,16 +6,31 @@ const basic_1 = require("../../types/basic");
|
|
|
6
6
|
const inline_data_1 = require("../expressions/inline_data");
|
|
7
7
|
const source_1 = require("../expressions/source");
|
|
8
8
|
const target_1 = require("../expressions/target");
|
|
9
|
+
const _type_utils_1 = require("../_type_utils");
|
|
9
10
|
class Split {
|
|
10
11
|
runSyntax(node, scope, filename) {
|
|
11
|
-
const
|
|
12
|
+
const intoTable = node.findTokenSequencePosition("INTO", "TABLE") !== undefined;
|
|
13
|
+
const type = intoTable ? new basic_1.TableType(new basic_1.StringType(), { withHeader: false }) : new basic_1.StringType();
|
|
12
14
|
for (const target of node.findAllExpressions(Expressions.Target)) {
|
|
13
15
|
const inline = target.findDirectExpression(Expressions.InlineData);
|
|
14
16
|
if (inline) {
|
|
15
17
|
new inline_data_1.InlineData().runSyntax(inline, scope, filename, type);
|
|
16
18
|
}
|
|
17
19
|
else {
|
|
18
|
-
new target_1.Target().runSyntax(target, scope, filename);
|
|
20
|
+
let targetType = new target_1.Target().runSyntax(target, scope, filename);
|
|
21
|
+
if (intoTable) {
|
|
22
|
+
if (!(targetType instanceof basic_1.TableType)
|
|
23
|
+
&& !(targetType instanceof basic_1.UnknownType)
|
|
24
|
+
&& !(targetType instanceof basic_1.VoidType)) {
|
|
25
|
+
throw new Error("Into must be table typed");
|
|
26
|
+
}
|
|
27
|
+
if (targetType instanceof basic_1.TableType) {
|
|
28
|
+
targetType = targetType.getRowType();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (new _type_utils_1.TypeUtils(scope).isCharLikeStrict(targetType) === false) {
|
|
32
|
+
throw new Error("Incompatible, target not character like");
|
|
33
|
+
}
|
|
19
34
|
}
|
|
20
35
|
}
|
|
21
36
|
for (const s of node.findDirectExpressions(Expressions.Source)) {
|
package/build/src/registry.js
CHANGED
|
@@ -191,6 +191,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
191
191
|
if (found) {
|
|
192
192
|
return found;
|
|
193
193
|
}
|
|
194
|
+
found = this.moveWithTableTarget(low, high, lowFile, highSyntax);
|
|
195
|
+
if (found) {
|
|
196
|
+
return found;
|
|
197
|
+
}
|
|
194
198
|
found = this.downportSelectInline(low, high, lowFile, highSyntax);
|
|
195
199
|
if (found) {
|
|
196
200
|
return found;
|
|
@@ -259,6 +263,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
259
263
|
if (found) {
|
|
260
264
|
return found;
|
|
261
265
|
}
|
|
266
|
+
found = this.replaceContains(high, lowFile, highSyntax);
|
|
267
|
+
if (found) {
|
|
268
|
+
return found;
|
|
269
|
+
}
|
|
262
270
|
found = this.replaceTableExpression(high, lowFile, highSyntax);
|
|
263
271
|
if (found) {
|
|
264
272
|
return found;
|
|
@@ -704,6 +712,37 @@ ${indentation}RAISE EXCEPTION ${uniqueName2}.`;
|
|
|
704
712
|
const fix = edit_helper_1.EditHelper.replaceRange(lowFile, start, end, code);
|
|
705
713
|
return issue_1.Issue.atToken(lowFile, high.getFirstToken(), "Downport, Reduce statement", this.getMetadata().key, this.conf.severity, fix);
|
|
706
714
|
}
|
|
715
|
+
moveWithTableTarget(node, high, lowFile, highSyntax) {
|
|
716
|
+
if (!(high.get() instanceof Statements.Move)) {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
const target = high.findDirectExpression(Expressions.Target);
|
|
720
|
+
if (target === undefined) {
|
|
721
|
+
return undefined;
|
|
722
|
+
}
|
|
723
|
+
const tableExpression = target.findDirectExpression(Expressions.TableExpression);
|
|
724
|
+
if (tableExpression === undefined) {
|
|
725
|
+
return undefined;
|
|
726
|
+
}
|
|
727
|
+
const index = tableExpression.findDirectExpression(Expressions.Source);
|
|
728
|
+
if (index === undefined) {
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
let uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
732
|
+
uniqueName = `<${uniqueName}>`;
|
|
733
|
+
const tName = target.concatTokens().split("[")[0];
|
|
734
|
+
const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
|
|
735
|
+
const code = `FIELD-SYMBOLS ${uniqueName} LIKE LINE OF ${tName}.
|
|
736
|
+
${indentation}READ TABLE ${tName} INDEX ${index === null || index === void 0 ? void 0 : index.concatTokens()} ASSIGNING <temp1>.
|
|
737
|
+
${indentation}IF sy-subrc <> 0.
|
|
738
|
+
${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
|
|
739
|
+
${indentation}ENDIF.
|
|
740
|
+
${uniqueName}`;
|
|
741
|
+
const start = target.getFirstToken().getStart();
|
|
742
|
+
const end = target.getLastToken().getEnd();
|
|
743
|
+
const fix = edit_helper_1.EditHelper.replaceRange(lowFile, start, end, code);
|
|
744
|
+
return issue_1.Issue.atToken(lowFile, high.getFirstToken(), "Downport, Reduce statement", this.getMetadata().key, this.conf.severity, fix);
|
|
745
|
+
}
|
|
707
746
|
moveWithOperator(high, lowFile) {
|
|
708
747
|
var _a, _b, _c;
|
|
709
748
|
if (!(high.get() instanceof Statements.Move)) {
|
|
@@ -1034,6 +1073,7 @@ ${indentation} output = ${topTarget}.`;
|
|
|
1034
1073
|
let structureName = uniqueName;
|
|
1035
1074
|
let added = false;
|
|
1036
1075
|
let data = "";
|
|
1076
|
+
let previous = undefined;
|
|
1037
1077
|
for (const b of (valueBody === null || valueBody === void 0 ? void 0 : valueBody.getChildren()) || []) {
|
|
1038
1078
|
if (b.concatTokens() === "(" && added === false) {
|
|
1039
1079
|
structureName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
|
|
@@ -1053,8 +1093,13 @@ ${indentation} output = ${topTarget}.`;
|
|
|
1053
1093
|
body += this.outlineLet(b, indentation, highSyntax, lowFile);
|
|
1054
1094
|
}
|
|
1055
1095
|
else if (b.concatTokens() === ")") {
|
|
1096
|
+
if (added === false && (previous === null || previous === void 0 ? void 0 : previous.concatTokens()) === "(") {
|
|
1097
|
+
body += data;
|
|
1098
|
+
added = true;
|
|
1099
|
+
}
|
|
1056
1100
|
body += indentation + `APPEND ${structureName} TO ${uniqueName}.\n`;
|
|
1057
1101
|
}
|
|
1102
|
+
previous = b;
|
|
1058
1103
|
}
|
|
1059
1104
|
if (forLoop !== undefined) {
|
|
1060
1105
|
indentation = indentation.substring(2);
|
|
@@ -1332,6 +1377,43 @@ ${indentation} output = ${topTarget}.`;
|
|
|
1332
1377
|
}
|
|
1333
1378
|
return undefined;
|
|
1334
1379
|
}
|
|
1380
|
+
replaceContains(node, lowFile, highSyntax) {
|
|
1381
|
+
const spag = highSyntax.spaghetti.lookupPosition(node.getFirstToken().getStart(), lowFile.getFilename());
|
|
1382
|
+
// only downport if its an single method call condition
|
|
1383
|
+
let found = false;
|
|
1384
|
+
for (const c of node.findAllExpressionsRecursive(Expressions.Compare)) {
|
|
1385
|
+
found = c.findDirectExpression(Expressions.MethodCallChain) !== undefined;
|
|
1386
|
+
if (found === true) {
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (found === false) {
|
|
1391
|
+
return undefined;
|
|
1392
|
+
}
|
|
1393
|
+
for (const r of (spag === null || spag === void 0 ? void 0 : spag.getData().references) || []) {
|
|
1394
|
+
if (r.referenceType !== _reference_1.ReferenceType.BuiltinMethodReference) {
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
const func = r.position.getName().toUpperCase();
|
|
1398
|
+
if (func === "CONTAINS") {
|
|
1399
|
+
const token = r.position.getToken();
|
|
1400
|
+
const expression = this.findMethodCallExpression(node, token);
|
|
1401
|
+
if (expression === undefined) {
|
|
1402
|
+
continue;
|
|
1403
|
+
}
|
|
1404
|
+
const sList = expression.findAllExpressions(Expressions.Source).map(e => e.concatTokens());
|
|
1405
|
+
if (sList.length !== 2) {
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
const code = sList[0] + " CS " + sList[1];
|
|
1409
|
+
const start = expression.getFirstToken().getStart();
|
|
1410
|
+
const end = expression.getLastToken().getEnd();
|
|
1411
|
+
const fix = edit_helper_1.EditHelper.replaceRange(lowFile, start, end, code);
|
|
1412
|
+
return issue_1.Issue.atToken(lowFile, token, "Downport contains()", this.getMetadata().key, this.conf.severity, fix);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
return undefined;
|
|
1416
|
+
}
|
|
1335
1417
|
replaceLineFunctions(node, lowFile, highSyntax) {
|
|
1336
1418
|
var _a, _b;
|
|
1337
1419
|
const spag = highSyntax.spaghetti.lookupPosition(node.getFirstToken().getStart(), lowFile.getFilename());
|
|
@@ -19,8 +19,10 @@ class MethodOverwritesBuiltIn extends _abap_rule_1.ABAPRule {
|
|
|
19
19
|
key: "method_overwrites_builtin",
|
|
20
20
|
title: "Method name overwrites builtin function",
|
|
21
21
|
shortDescription: `Checks Method names that overwrite builtin SAP functions`,
|
|
22
|
-
extendedInformation: `https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-us/abenbuilt_in_functions_overview.htm
|
|
23
|
-
|
|
22
|
+
extendedInformation: `https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-us/abenbuilt_in_functions_overview.htm
|
|
23
|
+
|
|
24
|
+
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#avoid-obscuring-built-in-functions`,
|
|
25
|
+
tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Styleguide],
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
getConfig() {
|
|
@@ -19,6 +19,7 @@ class PragmaPlacement extends _abap_rule_1.ABAPRule {
|
|
|
19
19
|
title: "Pragma Placement",
|
|
20
20
|
shortDescription: `Place pragmas at end of statements`,
|
|
21
21
|
tags: [_irule_1.RuleTag.SingleFile],
|
|
22
|
+
extendedInformation: `https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/abenpragma.htm`,
|
|
22
23
|
badExample: `DATA field ##NO_TEXT TYPE i.`,
|
|
23
24
|
goodExample: `DATA field TYPE i ##NO_TEXT.`,
|
|
24
25
|
};
|
|
@@ -42,7 +43,7 @@ class PragmaPlacement extends _abap_rule_1.ABAPRule {
|
|
|
42
43
|
}
|
|
43
44
|
if (children[children.length - 2].getLastToken().getStart().isAfter(p.getStart())) {
|
|
44
45
|
const message = "Place pragma at end of statement";
|
|
45
|
-
const issue = issue_1.Issue.
|
|
46
|
+
const issue = issue_1.Issue.atToken(file, p, message, this.getMetadata().key, this.conf.severity);
|
|
46
47
|
issues.push(issue);
|
|
47
48
|
continue; // max one finding per statement
|
|
48
49
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.85.
|
|
3
|
+
"version": "2.85.46",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -45,21 +45,21 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://abaplint.org",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@microsoft/api-extractor": "^7.19.
|
|
48
|
+
"@microsoft/api-extractor": "^7.19.5",
|
|
49
49
|
"@types/chai": "^4.3.0",
|
|
50
50
|
"@types/mocha": "^9.1.0",
|
|
51
|
-
"@types/node": "^17.0.
|
|
51
|
+
"@types/node": "^17.0.22",
|
|
52
52
|
"chai": "^4.3.6",
|
|
53
|
-
"eslint": "^8.
|
|
54
|
-
"mocha": "^9.2.
|
|
53
|
+
"eslint": "^8.11.0",
|
|
54
|
+
"mocha": "^9.2.2",
|
|
55
55
|
"c8": "^7.11.0",
|
|
56
56
|
"source-map-support": "^0.5.21",
|
|
57
|
-
"ts-json-schema-generator": "^0.
|
|
57
|
+
"ts-json-schema-generator": "^1.0.0",
|
|
58
58
|
"typescript": "^4.6.2"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"fast-xml-parser": "^4.0.
|
|
62
|
-
"json5": "^2.2.
|
|
61
|
+
"fast-xml-parser": "^4.0.7",
|
|
62
|
+
"json5": "^2.2.1",
|
|
63
63
|
"vscode-languageserver-protocol": "^3.16.0",
|
|
64
64
|
"vscode-languageserver-types": "^3.16.0"
|
|
65
65
|
}
|