@abaplint/core 2.93.99 → 2.94.0
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/1_lexer/lexer.js +38 -39
- package/build/src/abap/flow/flow_graph.js +18 -6
- package/build/src/abap/flow/statement_flow.js +1 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/if_in_if.js +22 -6
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/unnecessary_return.js +63 -0
- package/package.json +2 -2
|
@@ -3,15 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Lexer = void 0;
|
|
4
4
|
const position_1 = require("../../position");
|
|
5
5
|
const tokens_1 = require("./tokens");
|
|
6
|
-
var Mode;
|
|
7
|
-
(function (Mode) {
|
|
8
|
-
Mode[Mode["Normal"] = 0] = "Normal";
|
|
9
|
-
Mode[Mode["Ping"] = 1] = "Ping";
|
|
10
|
-
Mode[Mode["Str"] = 2] = "Str";
|
|
11
|
-
Mode[Mode["Template"] = 3] = "Template";
|
|
12
|
-
Mode[Mode["Comment"] = 4] = "Comment";
|
|
13
|
-
Mode[Mode["Pragma"] = 5] = "Pragma";
|
|
14
|
-
})(Mode || (Mode = {}));
|
|
15
6
|
class Buffer {
|
|
16
7
|
constructor() {
|
|
17
8
|
this.buf = "";
|
|
@@ -101,10 +92,18 @@ class Stream {
|
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
94
|
class Lexer {
|
|
95
|
+
constructor() {
|
|
96
|
+
this.ModeNormal = 1;
|
|
97
|
+
this.ModePing = 2;
|
|
98
|
+
this.ModeStr = 3;
|
|
99
|
+
this.ModeTemplate = 4;
|
|
100
|
+
this.ModeComment = 5;
|
|
101
|
+
this.ModePragma = 6;
|
|
102
|
+
}
|
|
104
103
|
run(file, virtual) {
|
|
105
104
|
this.virtual = virtual;
|
|
106
105
|
this.tokens = [];
|
|
107
|
-
this.m =
|
|
106
|
+
this.m = this.ModeNormal;
|
|
108
107
|
this.process(file.getRaw());
|
|
109
108
|
return { file, tokens: this.tokens };
|
|
110
109
|
}
|
|
@@ -130,13 +129,13 @@ class Lexer {
|
|
|
130
129
|
pos = new position_1.VirtualPosition(this.virtual, pos.getRow(), pos.getCol());
|
|
131
130
|
}
|
|
132
131
|
let tok = undefined;
|
|
133
|
-
if (this.m ===
|
|
132
|
+
if (this.m === this.ModeComment) {
|
|
134
133
|
tok = new tokens_1.Comment(pos, s);
|
|
135
134
|
}
|
|
136
|
-
else if (this.m ===
|
|
135
|
+
else if (this.m === this.ModePing || this.m === this.ModeStr) {
|
|
137
136
|
tok = new tokens_1.StringToken(pos, s);
|
|
138
137
|
}
|
|
139
|
-
else if (this.m ===
|
|
138
|
+
else if (this.m === this.ModeTemplate) {
|
|
140
139
|
const first = s.charAt(0);
|
|
141
140
|
const last = s.charAt(s.length - 1);
|
|
142
141
|
if (first === "|" && last === "|") {
|
|
@@ -308,39 +307,39 @@ class Lexer {
|
|
|
308
307
|
const ahead = this.stream.nextChar();
|
|
309
308
|
const aahead = this.stream.nextNextChar();
|
|
310
309
|
const prev = this.stream.prevChar();
|
|
311
|
-
if (ahead === "'" && this.m ===
|
|
310
|
+
if (ahead === "'" && this.m === this.ModeNormal) {
|
|
312
311
|
// start string
|
|
313
312
|
this.add();
|
|
314
|
-
this.m =
|
|
313
|
+
this.m = this.ModeStr;
|
|
315
314
|
}
|
|
316
315
|
else if ((ahead === "|" || ahead === "}")
|
|
317
|
-
&& this.m ===
|
|
316
|
+
&& this.m === this.ModeNormal) {
|
|
318
317
|
// start template
|
|
319
318
|
this.add();
|
|
320
|
-
this.m =
|
|
319
|
+
this.m = this.ModeTemplate;
|
|
321
320
|
}
|
|
322
|
-
else if (ahead === "`" && this.m ===
|
|
321
|
+
else if (ahead === "`" && this.m === this.ModeNormal) {
|
|
323
322
|
// start ping
|
|
324
323
|
this.add();
|
|
325
|
-
this.m =
|
|
324
|
+
this.m = this.ModePing;
|
|
326
325
|
}
|
|
327
|
-
else if (aahead === "##" && this.m ===
|
|
326
|
+
else if (aahead === "##" && this.m === this.ModeNormal) {
|
|
328
327
|
// start pragma
|
|
329
328
|
this.add();
|
|
330
|
-
this.m =
|
|
329
|
+
this.m = this.ModePragma;
|
|
331
330
|
}
|
|
332
331
|
else if ((ahead === "\"" || (ahead === "*" && current === "\n"))
|
|
333
|
-
&& this.m ===
|
|
332
|
+
&& this.m === this.ModeNormal) {
|
|
334
333
|
// start comment
|
|
335
334
|
this.add();
|
|
336
|
-
this.m =
|
|
335
|
+
this.m = this.ModeComment;
|
|
337
336
|
}
|
|
338
|
-
else if (this.m ===
|
|
337
|
+
else if (this.m === this.ModePragma && (ahead === "," || ahead === ":" || ahead === "." || ahead === " " || ahead === "\n")) {
|
|
339
338
|
// end of pragma
|
|
340
339
|
this.add();
|
|
341
|
-
this.m =
|
|
340
|
+
this.m = this.ModeNormal;
|
|
342
341
|
}
|
|
343
|
-
else if (this.m ===
|
|
342
|
+
else if (this.m === this.ModePing
|
|
344
343
|
&& buf.length > 1
|
|
345
344
|
&& current === "`"
|
|
346
345
|
&& aahead !== "``"
|
|
@@ -349,21 +348,21 @@ class Lexer {
|
|
|
349
348
|
// end of ping
|
|
350
349
|
this.add();
|
|
351
350
|
if (ahead === `"`) {
|
|
352
|
-
this.m =
|
|
351
|
+
this.m = this.ModeComment;
|
|
353
352
|
}
|
|
354
353
|
else {
|
|
355
|
-
this.m =
|
|
354
|
+
this.m = this.ModeNormal;
|
|
356
355
|
}
|
|
357
356
|
}
|
|
358
|
-
else if (this.m ===
|
|
357
|
+
else if (this.m === this.ModeTemplate
|
|
359
358
|
&& buf.length > 1
|
|
360
359
|
&& (current === "|" || current === "{")
|
|
361
360
|
&& (prev !== "\\" || this.stream.prevPrevChar() === "\\\\")) {
|
|
362
361
|
// end of template
|
|
363
362
|
this.add();
|
|
364
|
-
this.m =
|
|
363
|
+
this.m = this.ModeNormal;
|
|
365
364
|
}
|
|
366
|
-
else if (this.m ===
|
|
365
|
+
else if (this.m === this.ModeStr
|
|
367
366
|
&& current === "'"
|
|
368
367
|
&& buf.length > 1
|
|
369
368
|
&& aahead !== "''"
|
|
@@ -372,13 +371,13 @@ class Lexer {
|
|
|
372
371
|
// end of string
|
|
373
372
|
this.add();
|
|
374
373
|
if (ahead === "\"") {
|
|
375
|
-
this.m =
|
|
374
|
+
this.m = this.ModeComment;
|
|
376
375
|
}
|
|
377
376
|
else {
|
|
378
|
-
this.m =
|
|
377
|
+
this.m = this.ModeNormal;
|
|
379
378
|
}
|
|
380
379
|
}
|
|
381
|
-
else if (this.m ===
|
|
380
|
+
else if (this.m === this.ModeNormal
|
|
382
381
|
&& (ahead === " "
|
|
383
382
|
|| ahead === ":"
|
|
384
383
|
|| ahead === "."
|
|
@@ -396,21 +395,21 @@ class Lexer {
|
|
|
396
395
|
|| ahead === "\n")) {
|
|
397
396
|
this.add();
|
|
398
397
|
}
|
|
399
|
-
else if (ahead === "\n" && this.m !==
|
|
398
|
+
else if (ahead === "\n" && this.m !== this.ModeTemplate) {
|
|
400
399
|
this.add();
|
|
401
|
-
this.m =
|
|
400
|
+
this.m = this.ModeNormal;
|
|
402
401
|
}
|
|
403
|
-
else if (this.m ===
|
|
402
|
+
else if (this.m === this.ModeTemplate && current === "\n") {
|
|
404
403
|
this.add();
|
|
405
404
|
}
|
|
406
405
|
else if (current === ">"
|
|
407
406
|
&& (prev === "-" || prev === "=")
|
|
408
407
|
&& ahead !== " "
|
|
409
|
-
&& this.m ===
|
|
408
|
+
&& this.m === this.ModeNormal) {
|
|
410
409
|
// arrows
|
|
411
410
|
this.add();
|
|
412
411
|
}
|
|
413
|
-
else if (this.m ===
|
|
412
|
+
else if (this.m === this.ModeNormal
|
|
414
413
|
&& (buf === "."
|
|
415
414
|
|| buf === ","
|
|
416
415
|
|| buf === ":"
|
|
@@ -3,16 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FlowGraph = void 0;
|
|
4
4
|
class FlowGraph {
|
|
5
5
|
constructor(counter) {
|
|
6
|
-
this.label = "undefined";
|
|
7
6
|
this.edges = {};
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
7
|
+
this.label = "undefined";
|
|
8
|
+
this.startNode = "start#" + counter;
|
|
9
|
+
this.endNode = "end#" + counter;
|
|
10
10
|
}
|
|
11
11
|
getStart() {
|
|
12
|
-
return this.
|
|
12
|
+
return this.startNode;
|
|
13
13
|
}
|
|
14
14
|
getEnd() {
|
|
15
|
-
return this.
|
|
15
|
+
return this.endNode;
|
|
16
16
|
}
|
|
17
17
|
addEdge(from, to) {
|
|
18
18
|
if (this.edges[from] === undefined) {
|
|
@@ -38,6 +38,18 @@ class FlowGraph {
|
|
|
38
38
|
}
|
|
39
39
|
return list;
|
|
40
40
|
}
|
|
41
|
+
listInto(to, skipStart = true) {
|
|
42
|
+
const ret = [];
|
|
43
|
+
for (const e of this.listEdges()) {
|
|
44
|
+
if (skipStart === true && e.from === this.getStart()) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (e.to === to) {
|
|
48
|
+
ret.push(e.from);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return ret;
|
|
52
|
+
}
|
|
41
53
|
listNodes() {
|
|
42
54
|
const set = new Set();
|
|
43
55
|
for (const l of this.listEdges()) {
|
|
@@ -99,7 +111,7 @@ ${this.toTextEdges()}
|
|
|
99
111
|
}
|
|
100
112
|
return Array.from(set.values());
|
|
101
113
|
}
|
|
102
|
-
/** removes all nodes containing "#" that have one
|
|
114
|
+
/** removes all nodes containing "#" that have one in-going and one out-going edge */
|
|
103
115
|
reduce() {
|
|
104
116
|
for (const node of this.listNodes()) {
|
|
105
117
|
if (node.includes("#") === false) {
|
package/build/src/registry.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.IfInIf = exports.IfInIfConf = void 0;
|
|
4
4
|
const issue_1 = require("../issue");
|
|
5
|
+
const Statements = require("../abap/2_statements/statements");
|
|
5
6
|
const Structures = require("../abap/3_structures/structures");
|
|
6
7
|
const _abap_rule_1 = require("./_abap_rule");
|
|
7
8
|
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
8
9
|
const _irule_1 = require("./_irule");
|
|
10
|
+
const edit_helper_1 = require("../edit_helper");
|
|
9
11
|
class IfInIfConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
10
12
|
}
|
|
11
13
|
exports.IfInIfConf = IfInIfConf;
|
|
@@ -22,7 +24,7 @@ class IfInIf extends _abap_rule_1.ABAPRule {
|
|
|
22
24
|
extendedInformation: `
|
|
23
25
|
Directly nested IFs without ELSE can be refactored to a single condition using AND.
|
|
24
26
|
|
|
25
|
-
ELSE condtions with directly nested IF refactored to ELSEIF.
|
|
27
|
+
ELSE condtions with directly nested IF refactored to ELSEIF, quickfixes are suggested for this case.
|
|
26
28
|
|
|
27
29
|
https://docs.abapopenchecks.org/checks/01/
|
|
28
30
|
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#keep-the-nesting-depth-low`,
|
|
@@ -48,12 +50,9 @@ IF condition1.
|
|
|
48
50
|
ELSEIF condition2.
|
|
49
51
|
...
|
|
50
52
|
ENDIF.`,
|
|
51
|
-
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
53
|
+
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
|
-
getMessage() {
|
|
55
|
-
return "IF in IF. Use IF cond1 AND cond2 instead";
|
|
56
|
-
}
|
|
57
56
|
getConfig() {
|
|
58
57
|
return this.conf;
|
|
59
58
|
}
|
|
@@ -61,6 +60,7 @@ ENDIF.`,
|
|
|
61
60
|
this.conf = conf;
|
|
62
61
|
}
|
|
63
62
|
runParsed(file, obj) {
|
|
63
|
+
var _a, _b;
|
|
64
64
|
const issues = [];
|
|
65
65
|
if (obj.getType() === "INTF") {
|
|
66
66
|
return [];
|
|
@@ -69,6 +69,7 @@ ENDIF.`,
|
|
|
69
69
|
if (stru === undefined) {
|
|
70
70
|
return [];
|
|
71
71
|
}
|
|
72
|
+
let fixed = false;
|
|
72
73
|
let possible = stru.findAllStructures(Structures.If);
|
|
73
74
|
possible = possible.concat(stru.findAllStructures(Structures.Else));
|
|
74
75
|
for (const i of possible) {
|
|
@@ -94,8 +95,23 @@ ENDIF.`,
|
|
|
94
95
|
|| nestedIf.findDirectStructures(Structures.Else).length > 0)) {
|
|
95
96
|
continue;
|
|
96
97
|
}
|
|
98
|
+
let message = "IF in IF. Use IF cond1 AND cond2 instead";
|
|
99
|
+
let fix = undefined;
|
|
100
|
+
if (i.get() instanceof Structures.Else) {
|
|
101
|
+
message = "Change ELSE part to ELSEIF";
|
|
102
|
+
const els = i.findFirstStatement(Statements.Else);
|
|
103
|
+
const iff = (_a = i.findFirstStructure(Structures.If)) === null || _a === void 0 ? void 0 : _a.findDirectStatement(Statements.If);
|
|
104
|
+
const endif = (_b = i.findFirstStructure(Structures.If)) === null || _b === void 0 ? void 0 : _b.findDirectStatement(Statements.EndIf);
|
|
105
|
+
if (fixed === false && iff && els && endif) {
|
|
106
|
+
const fix1 = edit_helper_1.EditHelper.deleteRange(file, els.getLastToken().getStart(), iff === null || iff === void 0 ? void 0 : iff.getFirstToken().getStart());
|
|
107
|
+
const fix2 = edit_helper_1.EditHelper.deleteStatement(file, endif);
|
|
108
|
+
fix = edit_helper_1.EditHelper.merge(fix1, fix2);
|
|
109
|
+
// max one fix per file at a time
|
|
110
|
+
fixed = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
97
113
|
const token = i.getFirstToken();
|
|
98
|
-
const issue = issue_1.Issue.atToken(file, token,
|
|
114
|
+
const issue = issue_1.Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity, fix);
|
|
99
115
|
issues.push(issue);
|
|
100
116
|
}
|
|
101
117
|
return issues;
|
package/build/src/rules/index.js
CHANGED
|
@@ -151,6 +151,7 @@ __exportStar(require("./uncaught_exception"), exports);
|
|
|
151
151
|
__exportStar(require("./unknown_types"), exports);
|
|
152
152
|
__exportStar(require("./unnecessary_chaining"), exports);
|
|
153
153
|
__exportStar(require("./unnecessary_pragma"), exports);
|
|
154
|
+
__exportStar(require("./unnecessary_return"), exports);
|
|
154
155
|
__exportStar(require("./unreachable_code"), exports);
|
|
155
156
|
__exportStar(require("./unsecure_fae"), exports);
|
|
156
157
|
__exportStar(require("./unused_ddic"), exports);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnnecessaryReturn = exports.UnnecessaryReturnConf = void 0;
|
|
4
|
+
const issue_1 = require("../issue");
|
|
5
|
+
const _abap_rule_1 = require("./_abap_rule");
|
|
6
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
7
|
+
const _irule_1 = require("./_irule");
|
|
8
|
+
const Statements = require("../abap/2_statements/statements");
|
|
9
|
+
const edit_helper_1 = require("../edit_helper");
|
|
10
|
+
class UnnecessaryReturnConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
11
|
+
}
|
|
12
|
+
exports.UnnecessaryReturnConf = UnnecessaryReturnConf;
|
|
13
|
+
class UnnecessaryReturn extends _abap_rule_1.ABAPRule {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.conf = new UnnecessaryReturnConf();
|
|
17
|
+
}
|
|
18
|
+
getMetadata() {
|
|
19
|
+
return {
|
|
20
|
+
key: "unnecessary_return",
|
|
21
|
+
title: "Unnecessary Return",
|
|
22
|
+
shortDescription: `Finds unnecessary RETURN statements`,
|
|
23
|
+
extendedInformation: `Finds unnecessary RETURN statements`,
|
|
24
|
+
tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
|
|
25
|
+
badExample: `METHOD hello.
|
|
26
|
+
...
|
|
27
|
+
RETURN.
|
|
28
|
+
ENDMETHOD.`,
|
|
29
|
+
goodExample: `METHOD hello.
|
|
30
|
+
...
|
|
31
|
+
ENDMETHOD.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
getConfig() {
|
|
35
|
+
return this.conf;
|
|
36
|
+
}
|
|
37
|
+
setConfig(conf) {
|
|
38
|
+
this.conf = conf;
|
|
39
|
+
}
|
|
40
|
+
runParsed(file) {
|
|
41
|
+
const issues = [];
|
|
42
|
+
const structure = file.getStructure();
|
|
43
|
+
if (structure === undefined) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const statements = file.getStatements();
|
|
47
|
+
for (let i = 0; i < statements.length - 1; i++) {
|
|
48
|
+
const node = statements[i];
|
|
49
|
+
const next = statements[i + 1];
|
|
50
|
+
if (node.get() instanceof Statements.Return
|
|
51
|
+
&& (next.get() instanceof Statements.EndMethod
|
|
52
|
+
|| next.get() instanceof Statements.EndForm
|
|
53
|
+
|| next.get() instanceof Statements.EndFunction)) {
|
|
54
|
+
const message = "Unnecessary RETURN";
|
|
55
|
+
const fix = edit_helper_1.EditHelper.deleteStatement(file, node);
|
|
56
|
+
issues.push(issue_1.Issue.atStatement(file, node, message, this.getMetadata().key, this.getConfig().severity, fix));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return issues;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.UnnecessaryReturn = UnnecessaryReturn;
|
|
63
|
+
//# sourceMappingURL=unnecessary_return.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.94.0",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@microsoft/api-extractor": "^7.33.6",
|
|
50
50
|
"@types/chai": "^4.3.4",
|
|
51
|
-
"@types/mocha": "^10.0.
|
|
51
|
+
"@types/mocha": "^10.0.1",
|
|
52
52
|
"@types/node": "^18.11.9",
|
|
53
53
|
"chai": "^4.3.7",
|
|
54
54
|
"eslint": "^8.28.0",
|