@abaplint/core 2.93.98 → 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.
@@ -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 = "";
@@ -61,21 +52,36 @@ class Stream {
61
52
  return this.row;
62
53
  }
63
54
  prevChar() {
55
+ if (this.offset - 1 < 0) {
56
+ return "";
57
+ }
64
58
  return this.raw.substr(this.offset - 1, 1);
65
59
  }
66
60
  prevPrevChar() {
61
+ if (this.offset - 2 < 0) {
62
+ return "";
63
+ }
67
64
  return this.raw.substr(this.offset - 2, 2);
68
65
  }
69
66
  currentChar() {
70
67
  if (this.offset < 0) {
71
68
  return "\n"; // simulate newline at start of file to handle star(*) comments
72
69
  }
70
+ else if (this.offset >= this.raw.length) {
71
+ return "";
72
+ }
73
73
  return this.raw.substr(this.offset, 1);
74
74
  }
75
75
  nextChar() {
76
+ if (this.offset + 2 > this.raw.length) {
77
+ return "";
78
+ }
76
79
  return this.raw.substr(this.offset + 1, 1);
77
80
  }
78
81
  nextNextChar() {
82
+ if (this.offset + 3 > this.raw.length) {
83
+ return this.nextChar();
84
+ }
79
85
  return this.raw.substr(this.offset + 1, 2);
80
86
  }
81
87
  getRaw() {
@@ -86,10 +92,18 @@ class Stream {
86
92
  }
87
93
  }
88
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
+ }
89
103
  run(file, virtual) {
90
104
  this.virtual = virtual;
91
105
  this.tokens = [];
92
- this.m = Mode.Normal;
106
+ this.m = this.ModeNormal;
93
107
  this.process(file.getRaw());
94
108
  return { file, tokens: this.tokens };
95
109
  }
@@ -99,9 +113,11 @@ class Lexer {
99
113
  const col = this.stream.getCol();
100
114
  const row = this.stream.getRow();
101
115
  let whiteBefore = false;
102
- const prev = this.stream.getRaw().substr(this.stream.getOffset() - s.length, 1);
103
- if (prev === " " || prev === "\n" || prev === "\t" || prev === ":") {
104
- whiteBefore = true;
116
+ if (this.stream.getOffset() - s.length >= 0) {
117
+ const prev = this.stream.getRaw().substr(this.stream.getOffset() - s.length, 1);
118
+ if (prev === " " || prev === "\n" || prev === "\t" || prev === ":") {
119
+ whiteBefore = true;
120
+ }
105
121
  }
106
122
  let whiteAfter = false;
107
123
  const next = this.stream.nextChar();
@@ -113,13 +129,13 @@ class Lexer {
113
129
  pos = new position_1.VirtualPosition(this.virtual, pos.getRow(), pos.getCol());
114
130
  }
115
131
  let tok = undefined;
116
- if (this.m === Mode.Comment) {
132
+ if (this.m === this.ModeComment) {
117
133
  tok = new tokens_1.Comment(pos, s);
118
134
  }
119
- else if (this.m === Mode.Ping || this.m === Mode.Str) {
135
+ else if (this.m === this.ModePing || this.m === this.ModeStr) {
120
136
  tok = new tokens_1.StringToken(pos, s);
121
137
  }
122
- else if (this.m === Mode.Template) {
138
+ else if (this.m === this.ModeTemplate) {
123
139
  const first = s.charAt(0);
124
140
  const last = s.charAt(s.length - 1);
125
141
  if (first === "|" && last === "|") {
@@ -138,7 +154,7 @@ class Lexer {
138
154
  tok = new tokens_1.Identifier(pos, s);
139
155
  }
140
156
  }
141
- else if (s.substr(0, 2) === "##") {
157
+ else if (s.length > 2 && s.substr(0, 2) === "##") {
142
158
  tok = new tokens_1.Pragma(pos, s);
143
159
  }
144
160
  else if (s.length === 1) {
@@ -291,39 +307,39 @@ class Lexer {
291
307
  const ahead = this.stream.nextChar();
292
308
  const aahead = this.stream.nextNextChar();
293
309
  const prev = this.stream.prevChar();
294
- if (ahead === "'" && this.m === Mode.Normal) {
310
+ if (ahead === "'" && this.m === this.ModeNormal) {
295
311
  // start string
296
312
  this.add();
297
- this.m = Mode.Str;
313
+ this.m = this.ModeStr;
298
314
  }
299
315
  else if ((ahead === "|" || ahead === "}")
300
- && this.m === Mode.Normal) {
316
+ && this.m === this.ModeNormal) {
301
317
  // start template
302
318
  this.add();
303
- this.m = Mode.Template;
319
+ this.m = this.ModeTemplate;
304
320
  }
305
- else if (ahead === "`" && this.m === Mode.Normal) {
321
+ else if (ahead === "`" && this.m === this.ModeNormal) {
306
322
  // start ping
307
323
  this.add();
308
- this.m = Mode.Ping;
324
+ this.m = this.ModePing;
309
325
  }
310
- else if (aahead === "##" && this.m === Mode.Normal) {
326
+ else if (aahead === "##" && this.m === this.ModeNormal) {
311
327
  // start pragma
312
328
  this.add();
313
- this.m = Mode.Pragma;
329
+ this.m = this.ModePragma;
314
330
  }
315
331
  else if ((ahead === "\"" || (ahead === "*" && current === "\n"))
316
- && this.m === Mode.Normal) {
332
+ && this.m === this.ModeNormal) {
317
333
  // start comment
318
334
  this.add();
319
- this.m = Mode.Comment;
335
+ this.m = this.ModeComment;
320
336
  }
321
- else if (this.m === Mode.Pragma && (ahead === "," || ahead === ":" || ahead === "." || ahead === " " || ahead === "\n")) {
337
+ else if (this.m === this.ModePragma && (ahead === "," || ahead === ":" || ahead === "." || ahead === " " || ahead === "\n")) {
322
338
  // end of pragma
323
339
  this.add();
324
- this.m = Mode.Normal;
340
+ this.m = this.ModeNormal;
325
341
  }
326
- else if (this.m === Mode.Ping
342
+ else if (this.m === this.ModePing
327
343
  && buf.length > 1
328
344
  && current === "`"
329
345
  && aahead !== "``"
@@ -332,21 +348,21 @@ class Lexer {
332
348
  // end of ping
333
349
  this.add();
334
350
  if (ahead === `"`) {
335
- this.m = Mode.Comment;
351
+ this.m = this.ModeComment;
336
352
  }
337
353
  else {
338
- this.m = Mode.Normal;
354
+ this.m = this.ModeNormal;
339
355
  }
340
356
  }
341
- else if (this.m === Mode.Template
357
+ else if (this.m === this.ModeTemplate
342
358
  && buf.length > 1
343
359
  && (current === "|" || current === "{")
344
360
  && (prev !== "\\" || this.stream.prevPrevChar() === "\\\\")) {
345
361
  // end of template
346
362
  this.add();
347
- this.m = Mode.Normal;
363
+ this.m = this.ModeNormal;
348
364
  }
349
- else if (this.m === Mode.Str
365
+ else if (this.m === this.ModeStr
350
366
  && current === "'"
351
367
  && buf.length > 1
352
368
  && aahead !== "''"
@@ -355,13 +371,13 @@ class Lexer {
355
371
  // end of string
356
372
  this.add();
357
373
  if (ahead === "\"") {
358
- this.m = Mode.Comment;
374
+ this.m = this.ModeComment;
359
375
  }
360
376
  else {
361
- this.m = Mode.Normal;
377
+ this.m = this.ModeNormal;
362
378
  }
363
379
  }
364
- else if (this.m === Mode.Normal
380
+ else if (this.m === this.ModeNormal
365
381
  && (ahead === " "
366
382
  || ahead === ":"
367
383
  || ahead === "."
@@ -379,21 +395,21 @@ class Lexer {
379
395
  || ahead === "\n")) {
380
396
  this.add();
381
397
  }
382
- else if (ahead === "\n" && this.m !== Mode.Template) {
398
+ else if (ahead === "\n" && this.m !== this.ModeTemplate) {
383
399
  this.add();
384
- this.m = Mode.Normal;
400
+ this.m = this.ModeNormal;
385
401
  }
386
- else if (this.m === Mode.Template && current === "\n") {
402
+ else if (this.m === this.ModeTemplate && current === "\n") {
387
403
  this.add();
388
404
  }
389
405
  else if (current === ">"
390
406
  && (prev === "-" || prev === "=")
391
407
  && ahead !== " "
392
- && this.m === Mode.Normal) {
408
+ && this.m === this.ModeNormal) {
393
409
  // arrows
394
410
  this.add();
395
411
  }
396
- else if (this.m === Mode.Normal
412
+ else if (this.m === this.ModeNormal
397
413
  && (buf === "."
398
414
  || buf === ","
399
415
  || buf === ":"
@@ -10,7 +10,7 @@ class Compare extends combi_1.Expression {
10
10
  const val = (0, combi_1.altPrio)(_1.FieldSub, _1.Constant);
11
11
  const list = (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WParenLeft), val, (0, combi_1.plus)((0, combi_1.seq)(",", val)), (0, combi_1.tok)(tokens_1.ParenRightW));
12
12
  const inn = (0, combi_1.seq)((0, combi_1.optPrio)("NOT"), "IN", (0, combi_1.altPrio)(_1.Source, list));
13
- const sopt = (0, combi_1.seq)("IS", (0, combi_1.optPrio)("NOT"), (0, combi_1.altPrio)("SUPPLIED", "BOUND", (0, combi_1.ver)(version_1.Version.v750, (0, combi_1.seq)("INSTANCE OF", _1.ClassName)), "REQUESTED", "INITIAL"));
13
+ const sopt = (0, combi_1.seq)("IS", (0, combi_1.optPrio)("NOT"), (0, combi_1.altPrio)("SUPPLIED", "BOUND", (0, combi_1.ver)(version_1.Version.v750, (0, combi_1.seq)("INSTANCE OF", _1.ClassName), version_1.Version.OpenABAP), "REQUESTED", "INITIAL"));
14
14
  const between = (0, combi_1.seq)((0, combi_1.optPrio)("NOT"), "BETWEEN", _1.Source, "AND", _1.Source);
15
15
  const predicate = (0, combi_1.ver)(version_1.Version.v740sp08, _1.MethodCallChain);
16
16
  const rett = (0, combi_1.seq)(_1.Source, (0, combi_1.altPrio)((0, combi_1.seq)(_1.CompareOperator, _1.Source), inn, between, sopt));
@@ -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.start = "start#" + counter;
9
- this.end = "end#" + counter;
7
+ this.label = "undefined";
8
+ this.startNode = "start#" + counter;
9
+ this.endNode = "end#" + counter;
10
10
  }
11
11
  getStart() {
12
- return this.start;
12
+ return this.startNode;
13
13
  }
14
14
  getEnd() {
15
- return this.end;
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 ingoing and one outgoing edge */
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) {
@@ -31,6 +31,7 @@ class StatementFlow {
31
31
  }
32
32
  return ret.map(f => f.reduce());
33
33
  }
34
+ ////////////////////
34
35
  findBody(f) {
35
36
  var _a;
36
37
  return ((_a = f.findDirectStructure(Structures.Body)) === null || _a === void 0 ? void 0 : _a.getChildren()) || [];
@@ -63,7 +63,7 @@ class Registry {
63
63
  }
64
64
  static abaplintVersion() {
65
65
  // magic, see build script "version.sh"
66
- return "2.93.98";
66
+ return "2.94.0";
67
67
  }
68
68
  getDDICReferences() {
69
69
  return this.references;
@@ -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, this.getMessage(), this.getMetadata().key, this.conf.severity);
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;
@@ -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.93.98",
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.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",