@abaplint/core 2.113.218 → 2.113.220
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/statements/modify_entities.js +3 -2
- package/build/src/abap/2_statements/statements/read_entities.js +2 -1
- package/build/src/abap/5_syntax/_builtin.js +3 -1
- package/build/src/abap/5_syntax/basic_types.js +1 -1
- package/build/src/abap/5_syntax/statements/class_implementation.js +1 -1
- package/build/src/cds/expressions/cds_function_input.js +1 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/align_type_expressions.js +21 -0
- package/build/src/rules/identical_contents.js +38 -9
- package/build/src/rules/implement_methods.js +18 -2
- package/build/src/rules/parser_702_chaining.js +38 -3
- package/package.json +5 -5
|
@@ -15,7 +15,8 @@ class ModifyEntities {
|
|
|
15
15
|
const updateFrom = (0, combi_1.seq)("UPDATE FROM", expressions_1.Source, (0, combi_1.opt)(relating));
|
|
16
16
|
const deleteFrom = (0, combi_1.seq)("DELETE FROM", expressions_1.Source);
|
|
17
17
|
const updateFields = (0, combi_1.seq)("UPDATE", fieldsWith);
|
|
18
|
-
const
|
|
18
|
+
const updateSetFields = (0, combi_1.seq)("UPDATE SET FIELDS WITH", expressions_1.Source);
|
|
19
|
+
const operation = (0, combi_1.alt)(updateSetFields, (0, combi_1.seq)("CREATE SET FIELDS WITH", expressions_1.Source), updateFields, deleteFrom, updateFrom, create, execute, (0, combi_1.seq)("CREATE", (0, combi_1.opt)(by), (0, combi_1.optPrio)("AUTO FILL CID"), (0, combi_1.altPrio)(withh, fieldsWith)));
|
|
19
20
|
const failed = (0, combi_1.seq)("FAILED", expressions_1.Target);
|
|
20
21
|
const result = (0, combi_1.seq)("RESULT", expressions_1.Target);
|
|
21
22
|
const mapped = (0, combi_1.seq)("MAPPED", expressions_1.Target);
|
|
@@ -25,7 +26,7 @@ class ModifyEntities {
|
|
|
25
26
|
const create2 = (0, combi_1.seq)("CREATE", fieldsWith, (0, combi_1.opt)((0, combi_1.seq)("CREATE BY", expressions_1.AssociationName, fieldsWith)));
|
|
26
27
|
const create3 = (0, combi_1.seq)("CREATE BY", expressions_1.AssociationName, fieldsWith);
|
|
27
28
|
const create4 = (0, combi_1.seq)("CREATE FROM", expressions_1.Source, (0, combi_1.plus)((0, combi_1.seq)("CREATE BY", expressions_1.AssociationName, "FROM", expressions_1.Source)));
|
|
28
|
-
const entity = (0, combi_1.seq)("ENTITY", (0, combi_1.opt)("IN LOCAL MODE"), (0, combi_1.alt)(expressions_1.NamespaceSimpleName, expressions_1.EntityAssociation), (0, combi_1.alt)(execute, create, updateFields, deleteFrom, updateFrom, create2, create3, create4));
|
|
29
|
+
const entity = (0, combi_1.seq)("ENTITY", (0, combi_1.opt)("IN LOCAL MODE"), (0, combi_1.alt)(expressions_1.NamespaceSimpleName, expressions_1.EntityAssociation), (0, combi_1.alt)(execute, create, updateFields, deleteFrom, updateSetFields, updateFrom, create2, create3, create4));
|
|
29
30
|
return (0, combi_1.ver)(version_1.Version.v754, (0, combi_1.seq)("MODIFY", (0, combi_1.alt)(entities, entity), end));
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -13,7 +13,8 @@ class ReadEntities {
|
|
|
13
13
|
const result = (0, combi_1.seq)("RESULT", expressions_1.Target);
|
|
14
14
|
const failed = (0, combi_1.seq)("FAILED", expressions_1.Target);
|
|
15
15
|
const reported = (0, combi_1.seq)("REPORTED", expressions_1.Target);
|
|
16
|
-
const
|
|
16
|
+
const foo = (0, combi_1.seq)((0, combi_1.opt)((0, combi_1.seq)("BY", expressions_1.AssociationName)), (0, combi_1.alt)(fields, from, all), (0, combi_1.optPrio)(result));
|
|
17
|
+
const entity = (0, combi_1.seq)("ENTITY", expressions_1.NamespaceSimpleName, (0, combi_1.plus)(foo));
|
|
17
18
|
const s = (0, combi_1.seq)("ENTITIES OF", expressions_1.NamespaceSimpleName, (0, combi_1.opt)("IN LOCAL MODE"), (0, combi_1.plus)(entity), (0, combi_1.optPrio)((0, combi_1.seq)("LINK", expressions_1.Target)), (0, combi_1.optPrio)((0, combi_1.per)(failed, reported)));
|
|
18
19
|
const byall = (0, combi_1.seq)("BY", expressions_1.AssociationName, all);
|
|
19
20
|
const by = (0, combi_1.seq)("BY", expressions_1.AssociationName, fields);
|
|
@@ -364,7 +364,9 @@ class BuiltIn {
|
|
|
364
364
|
// https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abennews-610-system.htm
|
|
365
365
|
const id3 = new tokens_1.Identifier(new position_1.Position(this.row++, 1), "sy-repid");
|
|
366
366
|
const syrepid = new _typed_identifier_1.TypedIdentifier(id3, BuiltIn.filename, new basic_1.CharacterType(40, { qualifiedName: "sy-repid" }), ["read_only" /* IdentifierMeta.ReadOnly */, "built-in" /* IdentifierMeta.BuiltIn */]);
|
|
367
|
-
|
|
367
|
+
const id4 = new tokens_1.Identifier(new position_1.Position(this.row++, 1), "syst-repid");
|
|
368
|
+
const systrepid = new _typed_identifier_1.TypedIdentifier(id4, BuiltIn.filename, new basic_1.CharacterType(40, { qualifiedName: "syst-repid" }), ["read_only" /* IdentifierMeta.ReadOnly */, "built-in" /* IdentifierMeta.BuiltIn */]);
|
|
369
|
+
return [sy, syst, syrepid, systrepid];
|
|
368
370
|
}
|
|
369
371
|
buildConstant(name, type, value) {
|
|
370
372
|
const id = new tokens_1.Identifier(new position_1.Position(this.row++, 1), name);
|
|
@@ -422,7 +422,7 @@ class BasicTypes {
|
|
|
422
422
|
const name = typename.concatTokens();
|
|
423
423
|
const type = (_d = this.input.scope.getDDIC().lookupDDLS(name)) === null || _d === void 0 ? void 0 : _d.type;
|
|
424
424
|
if (type) {
|
|
425
|
-
return new Types.TableType(basic_1.VoidType.get("
|
|
425
|
+
return new Types.TableType(basic_1.VoidType.get("RAP-TODO"), options);
|
|
426
426
|
}
|
|
427
427
|
else if (this.input.scope.getDDIC().inErrorNamespace(name)) {
|
|
428
428
|
return new Types.UnknownType(`DDLS ${name} not found`);
|
|
@@ -32,7 +32,7 @@ class ClassImplementation {
|
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
34
|
// todo: instead of the void type, do proper typing, ie. only empty constructor method
|
|
35
|
-
input.scope.addIdentifier(new _typed_identifier_1.TypedIdentifier(new tokens_1.Identifier(new position_1.Position(1, 1), "super"), _builtin_1.BuiltIn.filename, basic_1.VoidType.get("
|
|
35
|
+
input.scope.addIdentifier(new _typed_identifier_1.TypedIdentifier(new tokens_1.Identifier(new position_1.Position(1, 1), "super"), _builtin_1.BuiltIn.filename, basic_1.VoidType.get("noSuperClass")));
|
|
36
36
|
}
|
|
37
37
|
input.scope.addIdentifier(new _typed_identifier_1.TypedIdentifier(new tokens_1.Identifier(new position_1.Position(1, 1), "me"), _builtin_1.BuiltIn.filename, new basic_1.ObjectReferenceType(classDefinition)));
|
|
38
38
|
helper.addAliasedAttributes(classDefinition); // todo, this is not correct, take care of instance vs static
|
|
@@ -6,7 +6,7 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
class CDSFunctionInput extends combi_1.Expression {
|
|
7
7
|
getRunnable() {
|
|
8
8
|
const qualified = (0, combi_1.seq)(_1.CDSName, (0, combi_1.opt)(_1.CDSParameters), (0, combi_1.starPrio)((0, combi_1.seq)(".", _1.CDSName, (0, combi_1.opt)(_1.CDSParameters))));
|
|
9
|
-
const input = (0, combi_1.altPrio)(_1.
|
|
9
|
+
const input = (0, combi_1.altPrio)(_1.CDSArithmetics, _1.CDSCast, _1.CDSFunction, _1.CDSCase, _1.CDSString, qualified, _1.CDSInteger);
|
|
10
10
|
return input;
|
|
11
11
|
}
|
|
12
12
|
}
|
package/build/src/registry.js
CHANGED
|
@@ -88,6 +88,7 @@ ENDINTERFACE.`,
|
|
|
88
88
|
}
|
|
89
89
|
issues.push(...this.checkTypes(stru, file));
|
|
90
90
|
issues.push(...this.checkMethods(stru, file));
|
|
91
|
+
issues.push(...this.checkEvents(stru, file));
|
|
91
92
|
return issues;
|
|
92
93
|
}
|
|
93
94
|
check(fields, column, file) {
|
|
@@ -147,6 +148,26 @@ ENDINTERFACE.`,
|
|
|
147
148
|
}
|
|
148
149
|
return issues;
|
|
149
150
|
}
|
|
151
|
+
checkEvents(stru, file) {
|
|
152
|
+
const issues = [];
|
|
153
|
+
const events = stru.findAllStatements(Statements.Events);
|
|
154
|
+
for (const e of events) {
|
|
155
|
+
const fields = [];
|
|
156
|
+
const params = e.findAllExpressions(Expressions.MethodParam);
|
|
157
|
+
let column = 0;
|
|
158
|
+
for (const p of params) {
|
|
159
|
+
const children = p.getChildren();
|
|
160
|
+
const name = children[children.length - 2];
|
|
161
|
+
fields.push({
|
|
162
|
+
nameEnd: name.getLastToken().getEnd(),
|
|
163
|
+
after: p.findFirstExpression(Expressions.TypeParam).getFirstToken().getStart()
|
|
164
|
+
});
|
|
165
|
+
column = Math.max(column, name.getFirstToken().getEnd().getCol() + 1);
|
|
166
|
+
}
|
|
167
|
+
issues.push(...this.check(fields, column, file));
|
|
168
|
+
}
|
|
169
|
+
return issues;
|
|
170
|
+
}
|
|
150
171
|
checkTypes(stru, file) {
|
|
151
172
|
const issues = [];
|
|
152
173
|
const types = stru.findAllStructuresRecursive(Structures.Types);
|
|
@@ -66,20 +66,51 @@ WRITE 'world'.`,
|
|
|
66
66
|
////////////////
|
|
67
67
|
analyzeIf(file, node) {
|
|
68
68
|
var _a;
|
|
69
|
-
|
|
69
|
+
const ifBody = node.findDirectStructure(Structures.Body);
|
|
70
|
+
const elseIfBodies = node.findDirectStructures(Structures.ElseIf) || [];
|
|
71
|
+
const elseBody = (_a = node.findDirectStructure(Structures.Else)) === null || _a === void 0 ? void 0 : _a.findDirectStructure(Structures.Body);
|
|
72
|
+
if (elseBody === undefined) {
|
|
70
73
|
return [];
|
|
71
74
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
75
|
+
const ifFirst = ifBody === null || ifBody === void 0 ? void 0 : ifBody.getFirstChild();
|
|
76
|
+
const elseFirst = elseBody === null || elseBody === void 0 ? void 0 : elseBody.getFirstChild();
|
|
77
|
+
const ifLast = ifBody === null || ifBody === void 0 ? void 0 : ifBody.getLastChild();
|
|
78
|
+
const elseLast = elseBody === null || elseBody === void 0 ? void 0 : elseBody.getLastChild();
|
|
79
|
+
if (elseIfBodies.length > 0) {
|
|
80
|
+
let firstMatch = true;
|
|
81
|
+
let lastMatch = true;
|
|
82
|
+
for (const elseif of elseIfBodies) {
|
|
83
|
+
const elseifBody = elseif.findDirectStructure(Structures.Body);
|
|
84
|
+
const elseifFirst = elseifBody === null || elseifBody === void 0 ? void 0 : elseifBody.getFirstChild();
|
|
85
|
+
const elseifLast = elseifBody === null || elseifBody === void 0 ? void 0 : elseifBody.getLastChild();
|
|
86
|
+
if (ifFirst === undefined
|
|
87
|
+
|| ifLast === undefined
|
|
88
|
+
|| elseLast === undefined
|
|
89
|
+
|| elseFirst === undefined) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (elseifFirst === undefined
|
|
93
|
+
|| ifFirst.concatTokens() !== elseifFirst.concatTokens()
|
|
94
|
+
|| elseFirst.concatTokens() !== elseifFirst.concatTokens()) {
|
|
95
|
+
firstMatch = false;
|
|
96
|
+
}
|
|
97
|
+
if (elseifLast === undefined
|
|
98
|
+
|| ifLast.concatTokens() !== elseifLast.concatTokens()
|
|
99
|
+
|| elseLast.concatTokens() !== elseifLast.concatTokens()) {
|
|
100
|
+
lastMatch = false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (firstMatch === true || lastMatch === true) {
|
|
104
|
+
const message = "Identical contents";
|
|
105
|
+
const issue = issue_1.Issue.atToken(file, node.getFirstToken(), message, this.getMetadata().key, this.conf.severity);
|
|
106
|
+
return [issue];
|
|
107
|
+
}
|
|
74
108
|
return [];
|
|
75
109
|
}
|
|
76
|
-
|
|
77
|
-
if (elseBody === undefined || ifBody === undefined) {
|
|
110
|
+
else if (elseBody === undefined || ifBody === undefined) {
|
|
78
111
|
return [];
|
|
79
112
|
}
|
|
80
113
|
{
|
|
81
|
-
const ifFirst = ifBody.getFirstChild();
|
|
82
|
-
const elseFirst = elseBody.getFirstChild();
|
|
83
114
|
if (ifFirst === undefined || elseFirst === undefined || this.isChained(ifFirst)) {
|
|
84
115
|
return [];
|
|
85
116
|
}
|
|
@@ -90,8 +121,6 @@ WRITE 'world'.`,
|
|
|
90
121
|
}
|
|
91
122
|
}
|
|
92
123
|
{
|
|
93
|
-
const ifLast = ifBody.getLastChild();
|
|
94
|
-
const elseLast = elseBody.getLastChild();
|
|
95
124
|
if (ifLast === undefined || elseLast === undefined || this.isChained(ifLast)) {
|
|
96
125
|
return [];
|
|
97
126
|
}
|
|
@@ -77,7 +77,15 @@ class ImplementMethods extends _abap_rule_1.ABAPRule {
|
|
|
77
77
|
const issue = issue_1.Issue.atIdentifier(found, "Do not implement abstract method \"" + md.name + "\"", this.getMetadata().key, this.conf.severity);
|
|
78
78
|
ret.push(issue);
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
if (def.isAbstract) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const message = "Abstract methods can only be defined in abstract classes.";
|
|
85
|
+
const issue = issue_1.Issue.atIdentifier(def.identifier, message, this.getMetadata().key, this.conf.severity);
|
|
86
|
+
ret.push(issue);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
81
89
|
}
|
|
82
90
|
if (impl === undefined) {
|
|
83
91
|
const message = "Class implementation for \"" + def.name + "\" not found";
|
|
@@ -181,7 +189,15 @@ class ImplementMethods extends _abap_rule_1.ABAPRule {
|
|
|
181
189
|
}
|
|
182
190
|
for (const m of this.findInterfaceMethods(idef)) {
|
|
183
191
|
if (this.isAbstract(m, interfaceInfo, def)) {
|
|
184
|
-
|
|
192
|
+
if (def.isAbstract) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const message = "Abstract methods can only be defined in abstract classes.";
|
|
197
|
+
const issue = issue_1.Issue.atIdentifier(def.identifier, message, this.getMetadata().key, this.conf.severity);
|
|
198
|
+
ret.push(issue);
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
185
201
|
}
|
|
186
202
|
if (this.isImplemented(m, def, impl) === false) {
|
|
187
203
|
const message = "Implement method \"" + m.method.name + "\" from interface \"" + m.objectName + "\"";
|
|
@@ -7,6 +7,7 @@ const _abap_rule_1 = require("./_abap_rule");
|
|
|
7
7
|
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
8
8
|
const _irule_1 = require("./_irule");
|
|
9
9
|
const version_1 = require("../version");
|
|
10
|
+
const __1 = require("..");
|
|
10
11
|
class Parser702ChainingConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
11
12
|
}
|
|
12
13
|
exports.Parser702ChainingConf = Parser702ChainingConf;
|
|
@@ -18,7 +19,7 @@ class Parser702Chaining extends _abap_rule_1.ABAPRule {
|
|
|
18
19
|
getMetadata() {
|
|
19
20
|
return {
|
|
20
21
|
key: "parser_702_chaining",
|
|
21
|
-
title: "Parser Error, bad
|
|
22
|
+
title: "Parser Error, bad chaining on 702",
|
|
22
23
|
shortDescription: `ABAP on 702 does not allow for method chaining with IMPORTING/EXPORTING/CHANGING keywords,
|
|
23
24
|
this rule finds these and reports errors.
|
|
24
25
|
Only active on target version 702 and below.`,
|
|
@@ -59,13 +60,47 @@ Only active on target version 702 and below.`,
|
|
|
59
60
|
|| param.findDirectTokenByText("CHANGING")
|
|
60
61
|
|| param.findDirectTokenByText("EXCEPTIONS")) {
|
|
61
62
|
const message = "This kind of method chaining not possible in 702";
|
|
62
|
-
|
|
63
|
-
issues.push(issue);
|
|
63
|
+
this.pushIssue(message, file, param, issues);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
// after a value assignment (move statement whose source is a method call, or method parameter assignment),
|
|
68
|
+
// there can't be any EXPORTING/IMPORTING/CHANGING/EXCEPTIONS
|
|
69
|
+
for (const statement of file.getStatements()) {
|
|
70
|
+
if (!(statement.get() instanceof __1.Statements.Move)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const source = statement.findDirectExpression(Expressions.Source);
|
|
74
|
+
if (source === undefined) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
this.ensureSourceHasNoProceduralKeywords(source, file, issues);
|
|
78
|
+
}
|
|
79
|
+
for (const methodParameters of stru.findAllExpressions(Expressions.MethodParameters)) {
|
|
80
|
+
for (const params of methodParameters.findAllExpressions(Expressions.ParameterS)) {
|
|
81
|
+
const source = params.findDirectExpression(Expressions.Source);
|
|
82
|
+
if (source === undefined) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
this.ensureSourceHasNoProceduralKeywords(source, file, issues);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
67
88
|
return issues;
|
|
68
89
|
}
|
|
90
|
+
ensureSourceHasNoProceduralKeywords(source, file, issues) {
|
|
91
|
+
const forbiddenTokens = ["EXPORTING", "IMPORTING", "CHANGING", "EXCEPTIONS"];
|
|
92
|
+
for (const param of source.findAllExpressions(Expressions.MethodParameters)) {
|
|
93
|
+
const usedForbiddenToken = forbiddenTokens.find(text => param.findDirectTokenByText(text));
|
|
94
|
+
if (usedForbiddenToken) {
|
|
95
|
+
const message = `Unexpected word ${usedForbiddenToken} in functional method call`;
|
|
96
|
+
this.pushIssue(message, file, param, issues);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
pushIssue(message, file, node, issues) {
|
|
101
|
+
const issue = issue_1.Issue.atPosition(file, node.getFirstToken().getStart(), message, this.getMetadata().key, this.conf.severity);
|
|
102
|
+
issues.push(issue);
|
|
103
|
+
}
|
|
69
104
|
}
|
|
70
105
|
exports.Parser702Chaining = Parser702Chaining;
|
|
71
106
|
//# sourceMappingURL=parser_702_chaining.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.113.
|
|
3
|
+
"version": "2.113.220",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
},
|
|
51
51
|
"homepage": "https://abaplint.org",
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@microsoft/api-extractor": "^7.52.
|
|
53
|
+
"@microsoft/api-extractor": "^7.52.15",
|
|
54
54
|
"@types/chai": "^4.3.20",
|
|
55
55
|
"@types/mocha": "^10.0.10",
|
|
56
|
-
"@types/node": "^24.
|
|
56
|
+
"@types/node": "^24.6.2",
|
|
57
57
|
"chai": "^4.5.0",
|
|
58
58
|
"eslint": "^9.36.0",
|
|
59
|
-
"mocha": "^11.7.
|
|
59
|
+
"mocha": "^11.7.4",
|
|
60
60
|
"c8": "^10.1.3",
|
|
61
61
|
"source-map-support": "^0.5.21",
|
|
62
62
|
"ts-json-schema-generator": "^2.4.0",
|
|
63
|
-
"typescript": "^5.9.
|
|
63
|
+
"typescript": "^5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"fast-xml-parser": "^5.2.5",
|