@abaplint/core 2.119.59 → 2.119.61
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/abaplint.d.ts +7 -0
- package/build/src/abap/1_lexer/lexer.js +84 -74
- package/build/src/abap/1_lexer/lexer_buffer.js +23 -9
- package/build/src/abap/1_lexer/lexer_stream.js +31 -16
- package/build/src/abap/2_statements/combi.js +34 -22
- package/build/src/abap/2_statements/result.js +51 -9
- package/build/src/abap/5_syntax/statements/delete_internal.js +10 -3
- package/build/src/config.js +7 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/dangerous_statement.js +6 -0
- package/build/src/rules/downport.js +1 -0
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -4405,6 +4405,10 @@ declare interface ISyntaxSettings {
|
|
|
4405
4405
|
* @uniqueItems true
|
|
4406
4406
|
*/
|
|
4407
4407
|
globalConstants?: string[];
|
|
4408
|
+
/** List of names to void in ambigious statements (regex not possible)
|
|
4409
|
+
* @uniqueItems true
|
|
4410
|
+
*/
|
|
4411
|
+
ambigiousVoids?: string[];
|
|
4408
4412
|
/** List of full named global macros (regex not possible)
|
|
4409
4413
|
* @uniqueItems true
|
|
4410
4414
|
*/
|
|
@@ -6403,10 +6407,13 @@ declare class Result {
|
|
|
6403
6407
|
private readonly tokens;
|
|
6404
6408
|
private readonly tokenIndex;
|
|
6405
6409
|
private nodes;
|
|
6410
|
+
private nodeCount;
|
|
6406
6411
|
constructor(tokens: readonly Token[], tokenIndex: number, nodes?: (ExpressionNode | TokenNode)[]);
|
|
6412
|
+
private static fromChain;
|
|
6407
6413
|
peek(): Token;
|
|
6408
6414
|
peekAt(offset: number): Token | undefined;
|
|
6409
6415
|
shift(node: ExpressionNode | TokenNode): Result;
|
|
6416
|
+
wrapConsumed(consumedTokens: number, node: ExpressionNode): Result;
|
|
6410
6417
|
popNode(): ExpressionNode | TokenNode | undefined;
|
|
6411
6418
|
getNodes(): (ExpressionNode | TokenNode)[];
|
|
6412
6419
|
setNodes(n: (ExpressionNode | TokenNode)[]): void;
|
|
@@ -12,6 +12,37 @@ const ModeStr = 3;
|
|
|
12
12
|
const ModeTemplate = 4;
|
|
13
13
|
const ModeComment = 5;
|
|
14
14
|
const ModePragma = 6;
|
|
15
|
+
// character codes, used for integer comparisons in the hot loop
|
|
16
|
+
const EOF = -1; // no character (start/end of file)
|
|
17
|
+
const CH_TAB = 9; // "\t"
|
|
18
|
+
const CH_NL = 10; // "\n"
|
|
19
|
+
const CH_SPACE = 32; // " "
|
|
20
|
+
const CH_DQUOTE = 34; // "\""
|
|
21
|
+
const CH_HASH = 35; // "#"
|
|
22
|
+
const CH_QUOTE = 39; // "'"
|
|
23
|
+
const CH_STAR = 42; // "*"
|
|
24
|
+
const CH_DASH = 45; // "-"
|
|
25
|
+
const CH_COMMA = 44; // ","
|
|
26
|
+
const CH_DOT = 46; // "."
|
|
27
|
+
const CH_COLON = 58; // ":"
|
|
28
|
+
const CH_EQUALS = 61; // "="
|
|
29
|
+
const CH_GT = 62; // ">"
|
|
30
|
+
const CH_AT = 64; // "@"
|
|
31
|
+
const CH_BACKSLASH = 92; // "\\"
|
|
32
|
+
const CH_BACKTICK = 96; // "`"
|
|
33
|
+
const CH_LBRACE = 123; // "{"
|
|
34
|
+
const CH_PIPE = 124; // "|"
|
|
35
|
+
const CH_RBRACE = 125; // "}"
|
|
36
|
+
// characters that terminate the current token
|
|
37
|
+
const SPLITS = new Set([
|
|
38
|
+
CH_SPACE, CH_COLON, CH_DOT, CH_COMMA, CH_DASH, 43 /* + */, 40 /* ( */,
|
|
39
|
+
41 /* ) */, 91 /* [ */, 93 /* ] */, CH_BACKSLASH, CH_TAB, CH_NL
|
|
40
|
+
]);
|
|
41
|
+
// single characters that are emitted as their own token
|
|
42
|
+
const BUFS = new Set([
|
|
43
|
+
CH_DOT, CH_COMMA, CH_COLON, 40 /* ( */, 41 /* ) */, 91 /* [ */,
|
|
44
|
+
93 /* ] */, 43 /* + */, CH_AT
|
|
45
|
+
]);
|
|
15
46
|
class Lexer {
|
|
16
47
|
run(file, virtual) {
|
|
17
48
|
this.virtual = virtual;
|
|
@@ -26,15 +57,17 @@ class Lexer {
|
|
|
26
57
|
const col = this.stream.getCol();
|
|
27
58
|
const row = this.stream.getRow();
|
|
28
59
|
let whiteBefore = false;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
const beforeOffset = this.stream.getOffset() - s.length;
|
|
61
|
+
if (beforeOffset >= 0) {
|
|
62
|
+
const prev = this.stream.charCodeAt(beforeOffset);
|
|
63
|
+
if (prev === CH_SPACE || prev === CH_NL || prev === CH_TAB || prev === CH_COLON) {
|
|
32
64
|
whiteBefore = true;
|
|
33
65
|
}
|
|
34
66
|
}
|
|
35
67
|
let whiteAfter = false;
|
|
36
68
|
const next = this.stream.nextChar();
|
|
37
|
-
if (next ===
|
|
69
|
+
if (next === CH_SPACE || next === CH_NL || next === CH_TAB || next === CH_COLON
|
|
70
|
+
|| next === CH_COMMA || next === CH_DOT || next === EOF || next === CH_DQUOTE) {
|
|
38
71
|
whiteAfter = true;
|
|
39
72
|
}
|
|
40
73
|
let pos = new position_1.Position(row, col - s.length);
|
|
@@ -204,10 +237,10 @@ class Lexer {
|
|
|
204
237
|
}
|
|
205
238
|
}
|
|
206
239
|
if (tok === undefined && this.m === ModeNormal && s.charAt(0) === "\\") {
|
|
207
|
-
const adj = this.stream.nextChar() ===
|
|
240
|
+
const adj = this.stream.nextChar() === EOF ? 1 : 0;
|
|
208
241
|
const prevOffset = this.stream.getOffset() - s.length - adj;
|
|
209
|
-
const prevChar =
|
|
210
|
-
const whiteBeforeBackslash = prevChar ===
|
|
242
|
+
const prevChar = this.stream.charCodeAt(prevOffset);
|
|
243
|
+
const whiteBeforeBackslash = prevChar === CH_SPACE || prevChar === CH_NL || prevChar === CH_TAB || prevChar === CH_COLON;
|
|
211
244
|
if (!whiteBeforeBackslash) {
|
|
212
245
|
tok = new tokens_1.AssociationName(pos, s);
|
|
213
246
|
}
|
|
@@ -220,100 +253,77 @@ class Lexer {
|
|
|
220
253
|
this.buffer.clear();
|
|
221
254
|
}
|
|
222
255
|
process(raw) {
|
|
223
|
-
|
|
224
|
-
this.
|
|
225
|
-
|
|
226
|
-
splits[" "] = true;
|
|
227
|
-
splits[":"] = true;
|
|
228
|
-
splits["."] = true;
|
|
229
|
-
splits[","] = true;
|
|
230
|
-
splits["-"] = true;
|
|
231
|
-
splits["+"] = true;
|
|
232
|
-
splits["("] = true;
|
|
233
|
-
splits[")"] = true;
|
|
234
|
-
splits["["] = true;
|
|
235
|
-
splits["]"] = true;
|
|
236
|
-
splits["\\"] = true;
|
|
237
|
-
splits["\t"] = true;
|
|
238
|
-
splits["\n"] = true;
|
|
239
|
-
const bufs = {};
|
|
240
|
-
bufs["."] = true;
|
|
241
|
-
bufs[","] = true;
|
|
242
|
-
bufs[":"] = true;
|
|
243
|
-
bufs["("] = true;
|
|
244
|
-
bufs[")"] = true;
|
|
245
|
-
bufs["["] = true;
|
|
246
|
-
bufs["]"] = true;
|
|
247
|
-
bufs["+"] = true;
|
|
248
|
-
bufs["@"] = true;
|
|
256
|
+
const stream = new lexer_stream_1.LexerStream(raw.replace(/\r/g, ""));
|
|
257
|
+
this.stream = stream;
|
|
258
|
+
this.buffer = new lexer_buffer_1.LexerBuffer(stream.getRaw());
|
|
249
259
|
for (;;) {
|
|
250
|
-
const current =
|
|
251
|
-
|
|
252
|
-
const ahead =
|
|
253
|
-
const aahead =
|
|
260
|
+
const current = stream.currentChar();
|
|
261
|
+
this.buffer.add(stream.getOffset());
|
|
262
|
+
const ahead = stream.nextChar();
|
|
263
|
+
const aahead = stream.nextNextChar();
|
|
254
264
|
if (this.m === ModeNormal) {
|
|
255
|
-
if (
|
|
265
|
+
if (SPLITS.has(ahead)) {
|
|
256
266
|
this.add();
|
|
257
267
|
}
|
|
258
|
-
else if (ahead ===
|
|
268
|
+
else if (ahead === CH_QUOTE) {
|
|
259
269
|
// start string
|
|
260
270
|
this.add();
|
|
261
271
|
this.m = ModeStr;
|
|
262
272
|
}
|
|
263
|
-
else if (ahead ===
|
|
273
|
+
else if (ahead === CH_PIPE || ahead === CH_RBRACE) {
|
|
264
274
|
// start template
|
|
265
275
|
this.add();
|
|
266
276
|
this.m = ModeTemplate;
|
|
267
277
|
}
|
|
268
|
-
else if (ahead ===
|
|
278
|
+
else if (ahead === CH_BACKTICK) {
|
|
269
279
|
// start ping
|
|
270
280
|
this.add();
|
|
271
281
|
this.m = ModePing;
|
|
272
282
|
}
|
|
273
|
-
else if (aahead ===
|
|
283
|
+
else if (ahead === CH_HASH && aahead === CH_HASH) {
|
|
274
284
|
// start pragma
|
|
275
285
|
this.add();
|
|
276
286
|
this.m = ModePragma;
|
|
277
287
|
}
|
|
278
|
-
else if (ahead ===
|
|
279
|
-
|| (ahead ===
|
|
288
|
+
else if (ahead === CH_DQUOTE
|
|
289
|
+
|| (ahead === CH_STAR && current === CH_NL)) {
|
|
280
290
|
// start comment
|
|
281
291
|
this.add();
|
|
282
292
|
this.m = ModeComment;
|
|
283
293
|
}
|
|
284
|
-
else if (ahead ===
|
|
294
|
+
else if (ahead === CH_AT && this.buffer.get().trim().length === 0) {
|
|
285
295
|
this.add();
|
|
286
296
|
}
|
|
287
|
-
else if (aahead ===
|
|
288
|
-
|| aahead === "=>") {
|
|
297
|
+
else if ((ahead === CH_DASH || ahead === CH_EQUALS) && aahead === CH_GT) {
|
|
289
298
|
this.add();
|
|
290
299
|
}
|
|
291
|
-
else if (current ===
|
|
292
|
-
&& ahead !==
|
|
293
|
-
&& (
|
|
300
|
+
else if (current === CH_GT
|
|
301
|
+
&& ahead !== CH_SPACE
|
|
302
|
+
&& (stream.prevChar() === CH_DASH || stream.prevChar() === CH_EQUALS)) {
|
|
294
303
|
// arrows
|
|
295
304
|
this.add();
|
|
296
305
|
}
|
|
297
|
-
else if (
|
|
298
|
-
&& (
|
|
299
|
-
|| (
|
|
306
|
+
else if (this.buffer.length() === 1
|
|
307
|
+
&& (BUFS.has(current)
|
|
308
|
+
|| (current === CH_DASH && ahead !== CH_GT))) {
|
|
300
309
|
this.add();
|
|
301
310
|
}
|
|
302
311
|
}
|
|
303
|
-
else if (this.m === ModePragma && (ahead ===
|
|
312
|
+
else if (this.m === ModePragma && (ahead === CH_COMMA || ahead === CH_COLON
|
|
313
|
+
|| ahead === CH_DOT || ahead === CH_SPACE || ahead === CH_NL)) {
|
|
304
314
|
// end of pragma
|
|
305
315
|
this.add();
|
|
306
316
|
this.m = ModeNormal;
|
|
307
317
|
}
|
|
308
318
|
else if (this.m === ModePing
|
|
309
|
-
&&
|
|
310
|
-
&& current ===
|
|
311
|
-
&& aahead
|
|
312
|
-
&& ahead !==
|
|
313
|
-
&& this.buffer.countIsEven(
|
|
319
|
+
&& this.buffer.length() > 1
|
|
320
|
+
&& current === CH_BACKTICK
|
|
321
|
+
&& !(ahead === CH_BACKTICK && aahead === CH_BACKTICK)
|
|
322
|
+
&& ahead !== CH_BACKTICK
|
|
323
|
+
&& this.buffer.countIsEven(CH_BACKTICK)) {
|
|
314
324
|
// end of ping
|
|
315
325
|
this.add();
|
|
316
|
-
if (ahead ===
|
|
326
|
+
if (ahead === CH_DQUOTE) {
|
|
317
327
|
this.m = ModeComment;
|
|
318
328
|
}
|
|
319
329
|
else {
|
|
@@ -321,41 +331,41 @@ class Lexer {
|
|
|
321
331
|
}
|
|
322
332
|
}
|
|
323
333
|
else if (this.m === ModeTemplate
|
|
324
|
-
&&
|
|
325
|
-
&& (current ===
|
|
326
|
-
&& (
|
|
334
|
+
&& this.buffer.length() > 1
|
|
335
|
+
&& (current === CH_PIPE || current === CH_LBRACE)
|
|
336
|
+
&& (stream.prevChar() !== CH_BACKSLASH || (stream.prevPrevChar() === CH_BACKSLASH && stream.prevChar() === CH_BACKSLASH))) {
|
|
327
337
|
// end of template
|
|
328
338
|
this.add();
|
|
329
339
|
this.m = ModeNormal;
|
|
330
340
|
}
|
|
331
341
|
else if (this.m === ModeTemplate
|
|
332
|
-
&& ahead ===
|
|
333
|
-
&& current !==
|
|
342
|
+
&& ahead === CH_RBRACE
|
|
343
|
+
&& current !== CH_BACKSLASH) {
|
|
334
344
|
this.add();
|
|
335
345
|
}
|
|
336
346
|
else if (this.m === ModeStr
|
|
337
|
-
&& current ===
|
|
338
|
-
&&
|
|
339
|
-
&& aahead
|
|
340
|
-
&& ahead !==
|
|
341
|
-
&& this.buffer.countIsEven(
|
|
347
|
+
&& current === CH_QUOTE
|
|
348
|
+
&& this.buffer.length() > 1
|
|
349
|
+
&& !(ahead === CH_QUOTE && aahead === CH_QUOTE)
|
|
350
|
+
&& ahead !== CH_QUOTE
|
|
351
|
+
&& this.buffer.countIsEven(CH_QUOTE)) {
|
|
342
352
|
// end of string
|
|
343
353
|
this.add();
|
|
344
|
-
if (ahead ===
|
|
354
|
+
if (ahead === CH_DQUOTE) {
|
|
345
355
|
this.m = ModeComment;
|
|
346
356
|
}
|
|
347
357
|
else {
|
|
348
358
|
this.m = ModeNormal;
|
|
349
359
|
}
|
|
350
360
|
}
|
|
351
|
-
else if (ahead ===
|
|
361
|
+
else if (ahead === CH_NL && this.m !== ModeTemplate) {
|
|
352
362
|
this.add();
|
|
353
363
|
this.m = ModeNormal;
|
|
354
364
|
}
|
|
355
|
-
else if (this.m === ModeTemplate && current ===
|
|
365
|
+
else if (this.m === ModeTemplate && current === CH_NL) {
|
|
356
366
|
this.add();
|
|
357
367
|
}
|
|
358
|
-
if (!
|
|
368
|
+
if (!stream.advance()) {
|
|
359
369
|
break;
|
|
360
370
|
}
|
|
361
371
|
}
|
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LexerBuffer = void 0;
|
|
4
|
+
// The buffer holds a reference to the raw input and tracks the [start, end)
|
|
5
|
+
// offset range of the current token, so the token string can be sliced out with
|
|
6
|
+
// a single substring() instead of being concatenated one character at a time.
|
|
4
7
|
class LexerBuffer {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.
|
|
8
|
+
constructor(raw) {
|
|
9
|
+
this.raw = raw;
|
|
10
|
+
this.start = 0;
|
|
11
|
+
this.end = 0;
|
|
12
|
+
this.empty = true;
|
|
7
13
|
}
|
|
8
|
-
add(
|
|
9
|
-
this.
|
|
10
|
-
|
|
14
|
+
add(offset) {
|
|
15
|
+
if (this.empty === true) {
|
|
16
|
+
this.start = offset < 0 ? 0 : offset;
|
|
17
|
+
this.empty = false;
|
|
18
|
+
}
|
|
19
|
+
this.end = offset + 1;
|
|
11
20
|
}
|
|
12
21
|
get() {
|
|
13
|
-
return this.
|
|
22
|
+
return this.raw.substring(this.start, this.end);
|
|
23
|
+
}
|
|
24
|
+
length() {
|
|
25
|
+
return this.end - this.start;
|
|
14
26
|
}
|
|
15
27
|
clear() {
|
|
16
|
-
this.
|
|
28
|
+
this.start = 0;
|
|
29
|
+
this.end = 0;
|
|
30
|
+
this.empty = true;
|
|
17
31
|
}
|
|
18
32
|
countIsEven(char) {
|
|
19
33
|
let count = 0;
|
|
20
|
-
for (let i =
|
|
21
|
-
if (this.
|
|
34
|
+
for (let i = this.start; i < this.end; i += 1) {
|
|
35
|
+
if (this.raw.charCodeAt(i) === char) {
|
|
22
36
|
count += 1;
|
|
23
37
|
}
|
|
24
38
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LexerStream = void 0;
|
|
4
|
+
const NL = 10; // "\n"
|
|
5
|
+
const EOF = -1; // no character (start/end of file)
|
|
4
6
|
class LexerStream {
|
|
5
7
|
constructor(raw) {
|
|
6
8
|
this.offset = -1;
|
|
@@ -9,7 +11,7 @@ class LexerStream {
|
|
|
9
11
|
this.col = 0;
|
|
10
12
|
}
|
|
11
13
|
advance() {
|
|
12
|
-
if (this.currentChar() ===
|
|
14
|
+
if (this.currentChar() === NL) {
|
|
13
15
|
this.col = 1;
|
|
14
16
|
this.row = this.row + 1;
|
|
15
17
|
}
|
|
@@ -27,38 +29,51 @@ class LexerStream {
|
|
|
27
29
|
getRow() {
|
|
28
30
|
return this.row;
|
|
29
31
|
}
|
|
32
|
+
// the *Char() accessors return character codes (charCodeAt) rather than
|
|
33
|
+
// single character strings, to avoid allocating a string per input character
|
|
34
|
+
// in the lexer hot loop. EOF (-1) is returned when the offset is out of range.
|
|
30
35
|
prevChar() {
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
const o = this.offset - 1;
|
|
37
|
+
if (o < 0) {
|
|
38
|
+
return EOF;
|
|
33
39
|
}
|
|
34
|
-
return this.raw.
|
|
40
|
+
return this.raw.charCodeAt(o);
|
|
35
41
|
}
|
|
36
42
|
prevPrevChar() {
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
const o = this.offset - 2;
|
|
44
|
+
if (o < 0) {
|
|
45
|
+
return EOF;
|
|
39
46
|
}
|
|
40
|
-
return this.raw.
|
|
47
|
+
return this.raw.charCodeAt(o);
|
|
41
48
|
}
|
|
42
49
|
currentChar() {
|
|
43
50
|
if (this.offset < 0) {
|
|
44
|
-
return
|
|
51
|
+
return NL; // simulate newline at start of file to handle star(*) comments
|
|
45
52
|
}
|
|
46
53
|
else if (this.offset >= this.raw.length) {
|
|
47
|
-
return
|
|
54
|
+
return EOF;
|
|
48
55
|
}
|
|
49
|
-
return this.raw.
|
|
56
|
+
return this.raw.charCodeAt(this.offset);
|
|
50
57
|
}
|
|
51
58
|
nextChar() {
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
const o = this.offset + 1;
|
|
60
|
+
if (o >= this.raw.length) {
|
|
61
|
+
return EOF;
|
|
54
62
|
}
|
|
55
|
-
return this.raw.
|
|
63
|
+
return this.raw.charCodeAt(o);
|
|
56
64
|
}
|
|
57
65
|
nextNextChar() {
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
const o = this.offset + 2;
|
|
67
|
+
if (o >= this.raw.length) {
|
|
68
|
+
return EOF;
|
|
60
69
|
}
|
|
61
|
-
return this.raw.
|
|
70
|
+
return this.raw.charCodeAt(o);
|
|
71
|
+
}
|
|
72
|
+
charCodeAt(o) {
|
|
73
|
+
if (o < 0 || o >= this.raw.length) {
|
|
74
|
+
return EOF;
|
|
75
|
+
}
|
|
76
|
+
return this.raw.charCodeAt(o);
|
|
62
77
|
}
|
|
63
78
|
getRaw() {
|
|
64
79
|
return this.raw;
|
|
@@ -123,8 +123,9 @@ class Word {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
class Token {
|
|
126
|
-
constructor(
|
|
127
|
-
this.
|
|
126
|
+
constructor(t) {
|
|
127
|
+
this.tokenType = t;
|
|
128
|
+
this.name = t.name;
|
|
128
129
|
}
|
|
129
130
|
listKeywords() {
|
|
130
131
|
return [];
|
|
@@ -136,7 +137,7 @@ class Token {
|
|
|
136
137
|
const result = [];
|
|
137
138
|
for (const input of r) {
|
|
138
139
|
if (input.remainingLength() !== 0
|
|
139
|
-
&& input.peek().constructor
|
|
140
|
+
&& input.peek().constructor === this.tokenType) {
|
|
140
141
|
result.push(input.shift(new nodes_1.TokenNode(input.peek())));
|
|
141
142
|
}
|
|
142
143
|
}
|
|
@@ -332,7 +333,12 @@ class Optional {
|
|
|
332
333
|
for (const input of r) {
|
|
333
334
|
result.push(input);
|
|
334
335
|
const res = this.optional.run([input]);
|
|
335
|
-
|
|
336
|
+
if (res.length === 1) {
|
|
337
|
+
result.push(res[0]);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
result.push(...res);
|
|
341
|
+
}
|
|
336
342
|
}
|
|
337
343
|
return result;
|
|
338
344
|
}
|
|
@@ -371,6 +377,9 @@ class Star {
|
|
|
371
377
|
// avoid stack overflow
|
|
372
378
|
result = result.concat(res);
|
|
373
379
|
}
|
|
380
|
+
else if (res.length === 1) {
|
|
381
|
+
result.push(res[0]);
|
|
382
|
+
}
|
|
374
383
|
else {
|
|
375
384
|
result.push(...res);
|
|
376
385
|
}
|
|
@@ -522,6 +531,9 @@ class Sequence {
|
|
|
522
531
|
// avoid stack overflow
|
|
523
532
|
result = result.concat(temp);
|
|
524
533
|
}
|
|
534
|
+
else if (temp.length === 1) {
|
|
535
|
+
result.push(temp[0]);
|
|
536
|
+
}
|
|
525
537
|
else {
|
|
526
538
|
result.push(...temp);
|
|
527
539
|
}
|
|
@@ -587,24 +599,14 @@ class Expression {
|
|
|
587
599
|
for (const input of r) {
|
|
588
600
|
const temp = this.runnable.run([input]);
|
|
589
601
|
for (const t of temp) {
|
|
590
|
-
|
|
602
|
+
const consumed = input.remainingLength() - t.remainingLength();
|
|
591
603
|
if (consumed > 0) {
|
|
592
|
-
const originalLength = t.getNodes().length;
|
|
593
|
-
const children = [];
|
|
594
|
-
while (consumed > 0) {
|
|
595
|
-
const sub = t.popNode();
|
|
596
|
-
if (sub) {
|
|
597
|
-
children.push(sub);
|
|
598
|
-
consumed = consumed - sub.countTokens();
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
604
|
const re = new nodes_1.ExpressionNode(this);
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
605
|
+
results.push(t.wrapConsumed(consumed, re));
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
results.push(t);
|
|
606
609
|
}
|
|
607
|
-
results.push(t);
|
|
608
610
|
}
|
|
609
611
|
}
|
|
610
612
|
// console.dir(results);
|
|
@@ -746,7 +748,12 @@ class Alternative {
|
|
|
746
748
|
const result = [];
|
|
747
749
|
for (const sequ of this.list) {
|
|
748
750
|
const temp = sequ.run(r);
|
|
749
|
-
|
|
751
|
+
if (temp.length === 1) {
|
|
752
|
+
result.push(temp[0]);
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
result.push(...temp);
|
|
756
|
+
}
|
|
750
757
|
}
|
|
751
758
|
return result;
|
|
752
759
|
}
|
|
@@ -804,7 +811,12 @@ class AlternativePriority {
|
|
|
804
811
|
// console.log(seq.toStr());
|
|
805
812
|
const temp = sequ.run(r);
|
|
806
813
|
if (temp.length > 0) {
|
|
807
|
-
|
|
814
|
+
if (temp.length === 1) {
|
|
815
|
+
result.push(temp[0]);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
result.push(...temp);
|
|
819
|
+
}
|
|
808
820
|
break;
|
|
809
821
|
}
|
|
810
822
|
}
|
|
@@ -920,7 +932,7 @@ function regex(r) {
|
|
|
920
932
|
return new Regex(r);
|
|
921
933
|
}
|
|
922
934
|
function tok(t) {
|
|
923
|
-
return new Token(t
|
|
935
|
+
return new Token(t);
|
|
924
936
|
}
|
|
925
937
|
const expressionSingletons = {};
|
|
926
938
|
const stringSingletons = {};
|
|
@@ -7,11 +7,17 @@ class Result {
|
|
|
7
7
|
// nodes: matched tokens
|
|
8
8
|
this.tokens = tokens;
|
|
9
9
|
this.tokenIndex = tokenIndex;
|
|
10
|
-
this.
|
|
11
|
-
if (
|
|
12
|
-
this.nodes
|
|
10
|
+
this.nodeCount = 0;
|
|
11
|
+
if (nodes !== undefined) {
|
|
12
|
+
this.setNodes(nodes);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
+
static fromChain(tokens, tokenIndex, nodes, nodeCount) {
|
|
16
|
+
const ret = new Result(tokens, tokenIndex);
|
|
17
|
+
ret.nodes = nodes;
|
|
18
|
+
ret.nodeCount = nodeCount;
|
|
19
|
+
return ret;
|
|
20
|
+
}
|
|
15
21
|
peek() {
|
|
16
22
|
return this.tokens[this.tokenIndex];
|
|
17
23
|
}
|
|
@@ -19,18 +25,54 @@ class Result {
|
|
|
19
25
|
return this.tokens[this.tokenIndex + offset];
|
|
20
26
|
}
|
|
21
27
|
shift(node) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
return Result.fromChain(this.tokens, this.tokenIndex + 1, { node, previous: this.nodes }, this.nodeCount + 1);
|
|
29
|
+
}
|
|
30
|
+
wrapConsumed(consumedTokens, node) {
|
|
31
|
+
let current = this.nodes;
|
|
32
|
+
let currentCount = this.nodeCount;
|
|
33
|
+
const children = [];
|
|
34
|
+
while (consumedTokens > 0) {
|
|
35
|
+
if (current === undefined) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
children.push(current.node);
|
|
39
|
+
consumedTokens = consumedTokens - current.node.countTokens();
|
|
40
|
+
current = current.previous;
|
|
41
|
+
currentCount--;
|
|
42
|
+
}
|
|
43
|
+
node.setChildren(children.reverse());
|
|
44
|
+
this.nodes = { node, previous: current };
|
|
45
|
+
this.nodeCount = currentCount + 1;
|
|
46
|
+
return this;
|
|
25
47
|
}
|
|
26
48
|
popNode() {
|
|
27
|
-
|
|
49
|
+
if (this.nodes === undefined) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const ret = this.nodes.node;
|
|
53
|
+
this.nodes = this.nodes.previous;
|
|
54
|
+
this.nodeCount--;
|
|
55
|
+
return ret;
|
|
28
56
|
}
|
|
29
57
|
getNodes() {
|
|
30
|
-
|
|
58
|
+
const ret = new Array(this.nodeCount);
|
|
59
|
+
let current = this.nodes;
|
|
60
|
+
for (let index = this.nodeCount - 1; index >= 0; index--) {
|
|
61
|
+
if (current === undefined) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
ret[index] = current.node;
|
|
65
|
+
current = current.previous;
|
|
66
|
+
}
|
|
67
|
+
return ret;
|
|
31
68
|
}
|
|
32
69
|
setNodes(n) {
|
|
33
|
-
this.nodes =
|
|
70
|
+
this.nodes = undefined;
|
|
71
|
+
this.nodeCount = 0;
|
|
72
|
+
for (const node of n) {
|
|
73
|
+
this.nodes = { node, previous: this.nodes };
|
|
74
|
+
this.nodeCount++;
|
|
75
|
+
}
|
|
34
76
|
}
|
|
35
77
|
getTokens() {
|
|
36
78
|
return this.tokens;
|
|
@@ -50,18 +50,25 @@ class DeleteInternal {
|
|
|
50
50
|
let targetType = undefined;
|
|
51
51
|
const target = node.findDirectExpression(Expressions.Target);
|
|
52
52
|
if (target) {
|
|
53
|
+
const targetName = target.concatTokens();
|
|
53
54
|
let tabl = undefined;
|
|
54
|
-
const localVariable = input.scope.findVariable(
|
|
55
|
+
const localVariable = input.scope.findVariable(targetName);
|
|
55
56
|
if (localVariable === undefined && node.getChildren().length === 5 && node.getChildren()[2].concatTokens().toUpperCase() === "FROM") {
|
|
56
57
|
// it might be a database table
|
|
57
|
-
const found = (_a = input.scope.getDDIC()) === null || _a === void 0 ? void 0 : _a.lookupTableOrView(
|
|
58
|
+
const found = (_a = input.scope.getDDIC()) === null || _a === void 0 ? void 0 : _a.lookupTableOrView(targetName);
|
|
58
59
|
if ((found === null || found === void 0 ? void 0 : found.object) !== undefined) {
|
|
59
60
|
tabl = found;
|
|
60
61
|
input.scope.getDDICReferences().addUsing(input.scope.getParentObj(), { object: tabl.object });
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
if (tabl === undefined) {
|
|
64
|
-
|
|
65
|
+
const ambigiousVoids = input.scope.getRegistry().getConfig().getSyntaxSetttings().ambigiousVoids || [];
|
|
66
|
+
if (ambigiousVoids.some(name => name.toUpperCase() === targetName.toUpperCase())) {
|
|
67
|
+
targetType = basic_1.VoidType.get(targetName);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
targetType = target_1.Target.runSyntax(target, input);
|
|
71
|
+
}
|
|
65
72
|
if (node.findDirectTokenByText("TABLE") === undefined
|
|
66
73
|
&& node.findDirectTokenByText("ADJACENT") === undefined
|
|
67
74
|
&& (node.findDirectTokenByText("FROM") || node.findDirectTokenByText("INDEX"))
|
package/build/src/config.js
CHANGED
|
@@ -78,6 +78,7 @@ class Config {
|
|
|
78
78
|
languageVersion: langVer,
|
|
79
79
|
errorNamespace: "^(Z|Y|LCL\_|TY\_|LIF\_)",
|
|
80
80
|
globalConstants: [],
|
|
81
|
+
ambigiousVoids: [],
|
|
81
82
|
globalMacros: [],
|
|
82
83
|
},
|
|
83
84
|
rules: rules,
|
|
@@ -128,6 +129,12 @@ class Config {
|
|
|
128
129
|
// remove duplicates,
|
|
129
130
|
this.config.syntax.globalConstants = [...new Set(this.config.syntax.globalConstants)];
|
|
130
131
|
}
|
|
132
|
+
if (this.config.syntax.ambigiousVoids === undefined) {
|
|
133
|
+
this.config.syntax.ambigiousVoids = [];
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.config.syntax.ambigiousVoids = [...new Set(this.config.syntax.ambigiousVoids)];
|
|
137
|
+
}
|
|
131
138
|
if (this.config.global.skipIncludesWithoutMain === undefined) {
|
|
132
139
|
this.config.global.skipIncludesWithoutMain = false;
|
|
133
140
|
}
|
package/build/src/registry.js
CHANGED
|
@@ -59,6 +59,7 @@ class DangerousStatementConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
|
59
59
|
this.insertTextpool = true;
|
|
60
60
|
this.deleteDynpro = true;
|
|
61
61
|
this.exportDynpro = true;
|
|
62
|
+
this.editorCallForReport = true;
|
|
62
63
|
/** Finds instances of dynamic SQL: SELECT, UPDATE, DELETE, INSERT, MODIFY */
|
|
63
64
|
this.dynamicSQL = true;
|
|
64
65
|
/** Ignore dynamic SQL in IF_RAP_QUERY_PROVIDER~SELECT implementations */
|
|
@@ -143,6 +144,11 @@ dynamic SQL can potentially create SQL injection problems`,
|
|
|
143
144
|
else if (this.conf.exportDynpro && statement instanceof Statements.ExportDynpro) {
|
|
144
145
|
message = "EXPORT DYNPRO";
|
|
145
146
|
}
|
|
147
|
+
else if (this.conf.editorCallForReport
|
|
148
|
+
&& statement instanceof Statements.EditorCall
|
|
149
|
+
&& statementNode.findDirectTokenByText("REPORT")) {
|
|
150
|
+
message = "EDITOR-CALL FOR REPORT";
|
|
151
|
+
}
|
|
146
152
|
if (message) {
|
|
147
153
|
issues.push(issue_1.Issue.atStatement(file, statementNode, this.getDescription(message), this.getMetadata().key, this.conf.severity));
|
|
148
154
|
}
|
|
@@ -342,6 +342,7 @@ Make sure to test the downported code, it might not always be completely correct
|
|
|
342
342
|
const lowConfig = this.lowReg.getConfig().get();
|
|
343
343
|
highConfig.syntax.errorNamespace = lowConfig.syntax.errorNamespace;
|
|
344
344
|
highConfig.syntax.globalConstants = lowConfig.syntax.globalConstants;
|
|
345
|
+
highConfig.syntax.ambigiousVoids = lowConfig.syntax.ambigiousVoids;
|
|
345
346
|
highConfig.syntax.globalMacros = lowConfig.syntax.globalMacros;
|
|
346
347
|
this.highReg = new registry_1.Registry();
|
|
347
348
|
for (const o of this.lowReg.getObjects()) {
|