@abaplint/core 2.119.58 → 2.119.60
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 +3 -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 +29 -7
- package/build/src/abap/2_statements/statement_parser.js +12 -1
- package/build/src/abap/nodes/expression_node.js +9 -5
- package/build/src/abap/types/class_definition.js +8 -0
- package/build/src/cds/expressions/cds_define_table_function.js +1 -1
- package/build/src/cds/expressions/cds_element.js +1 -1
- package/build/src/cds/expressions/cds_name.js +1 -1
- package/build/src/cds/expressions/cds_string.js +1 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/dangerous_statement.js +6 -0
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -1251,6 +1251,7 @@ declare class ClassDefinition_3 extends Identifier implements IClassDefinition {
|
|
|
1251
1251
|
getCreateVisibility(): Visibility;
|
|
1252
1252
|
private findSuper;
|
|
1253
1253
|
private checkMethodNameLength;
|
|
1254
|
+
private checkClassConstructorStatic;
|
|
1254
1255
|
private checkMethodsFromSuperClasses;
|
|
1255
1256
|
private findFriends;
|
|
1256
1257
|
private addReference;
|
|
@@ -2430,9 +2431,11 @@ declare abstract class Expression implements IStatementRunnable {
|
|
|
2430
2431
|
|
|
2431
2432
|
declare class ExpressionNode extends AbstractNode<ExpressionNode | TokenNode> {
|
|
2432
2433
|
private readonly expression;
|
|
2434
|
+
private tokenCount;
|
|
2433
2435
|
constructor(expression: IStatementRunnable);
|
|
2434
2436
|
addChild(_n: ExpressionNode | TokenNode): void;
|
|
2435
2437
|
get(): IStatementRunnable;
|
|
2438
|
+
setChildren(children: (ExpressionNode | TokenNode)[]): ExpressionNode;
|
|
2436
2439
|
countTokens(): number;
|
|
2437
2440
|
getFirstToken(): Token;
|
|
2438
2441
|
concatTokensWithLinebreaks(): string;
|
|
@@ -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
|
}
|
|
@@ -746,7 +758,12 @@ class Alternative {
|
|
|
746
758
|
const result = [];
|
|
747
759
|
for (const sequ of this.list) {
|
|
748
760
|
const temp = sequ.run(r);
|
|
749
|
-
|
|
761
|
+
if (temp.length === 1) {
|
|
762
|
+
result.push(temp[0]);
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
result.push(...temp);
|
|
766
|
+
}
|
|
750
767
|
}
|
|
751
768
|
return result;
|
|
752
769
|
}
|
|
@@ -804,7 +821,12 @@ class AlternativePriority {
|
|
|
804
821
|
// console.log(seq.toStr());
|
|
805
822
|
const temp = sequ.run(r);
|
|
806
823
|
if (temp.length > 0) {
|
|
807
|
-
|
|
824
|
+
if (temp.length === 1) {
|
|
825
|
+
result.push(temp[0]);
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
result.push(...temp);
|
|
829
|
+
}
|
|
808
830
|
break;
|
|
809
831
|
}
|
|
810
832
|
}
|
|
@@ -920,7 +942,7 @@ function regex(r) {
|
|
|
920
942
|
return new Regex(r);
|
|
921
943
|
}
|
|
922
944
|
function tok(t) {
|
|
923
|
-
return new Token(t
|
|
945
|
+
return new Token(t);
|
|
924
946
|
}
|
|
925
947
|
const expressionSingletons = {};
|
|
926
948
|
const stringSingletons = {};
|
|
@@ -46,6 +46,7 @@ const expand_macros_1 = require("./expand_macros");
|
|
|
46
46
|
const tokens_1 = require("../1_lexer/tokens");
|
|
47
47
|
const _select_reclassify_1 = require("./_select_reclassify");
|
|
48
48
|
exports.STATEMENT_MAX_TOKENS = 20000;
|
|
49
|
+
const EMPTY_PRAGMAS = Object.freeze([]);
|
|
49
50
|
class StatementMap {
|
|
50
51
|
constructor() {
|
|
51
52
|
this.map = {};
|
|
@@ -244,9 +245,19 @@ class StatementParser {
|
|
|
244
245
|
return statement;
|
|
245
246
|
}
|
|
246
247
|
removePragma(tokens) {
|
|
248
|
+
let hasPragma = false;
|
|
249
|
+
// skip the last token as it is the punctuation
|
|
250
|
+
for (let i = 0; i < tokens.length - 1; i++) {
|
|
251
|
+
if (tokens[i] instanceof Tokens.Pragma) {
|
|
252
|
+
hasPragma = true;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (hasPragma === false) {
|
|
257
|
+
return { tokens: tokens.slice(0, tokens.length - 1), pragmas: EMPTY_PRAGMAS };
|
|
258
|
+
}
|
|
247
259
|
const result = [];
|
|
248
260
|
const pragmas = [];
|
|
249
|
-
// skip the last token as it is the punctuation
|
|
250
261
|
for (let i = 0; i < tokens.length - 1; i++) {
|
|
251
262
|
const t = tokens[i];
|
|
252
263
|
if (t instanceof Tokens.Pragma) {
|
|
@@ -7,6 +7,7 @@ const _abstract_node_1 = require("./_abstract_node");
|
|
|
7
7
|
class ExpressionNode extends _abstract_node_1.AbstractNode {
|
|
8
8
|
constructor(expression) {
|
|
9
9
|
super();
|
|
10
|
+
this.tokenCount = 0;
|
|
10
11
|
this.expression = expression;
|
|
11
12
|
}
|
|
12
13
|
addChild(_n) {
|
|
@@ -15,12 +16,15 @@ class ExpressionNode extends _abstract_node_1.AbstractNode {
|
|
|
15
16
|
get() {
|
|
16
17
|
return this.expression;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
for (const
|
|
21
|
-
|
|
19
|
+
setChildren(children) {
|
|
20
|
+
super.setChildren(children);
|
|
21
|
+
for (const child of this.getChildren()) {
|
|
22
|
+
this.tokenCount = this.tokenCount + child.countTokens();
|
|
22
23
|
}
|
|
23
|
-
return
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
countTokens() {
|
|
27
|
+
return this.tokenCount;
|
|
24
28
|
}
|
|
25
29
|
getFirstToken() {
|
|
26
30
|
for (const child of this.getChildren()) {
|
|
@@ -97,6 +97,7 @@ class ClassDefinition extends _identifier_1.Identifier {
|
|
|
97
97
|
// perform checks after everything has been initialized
|
|
98
98
|
this.checkMethodsFromSuperClasses(input);
|
|
99
99
|
this.checkMethodNameLength(input);
|
|
100
|
+
this.checkClassConstructorStatic(input);
|
|
100
101
|
}
|
|
101
102
|
getFriends() {
|
|
102
103
|
return this.friends;
|
|
@@ -160,6 +161,13 @@ class ClassDefinition extends _identifier_1.Identifier {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
}
|
|
164
|
+
checkClassConstructorStatic(input) {
|
|
165
|
+
for (const m of this.methodDefs.getAll()) {
|
|
166
|
+
if (m.getName().toUpperCase() === "CLASS_CONSTRUCTOR" && m.isStatic() === false) {
|
|
167
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, m.getToken(), "CLASS_CONSTRUCTOR must be static"));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
163
171
|
checkMethodsFromSuperClasses(input) {
|
|
164
172
|
var _a;
|
|
165
173
|
const scope = input.scope;
|
|
@@ -7,7 +7,7 @@ const cds_name_1 = require("./cds_name");
|
|
|
7
7
|
class CDSDefineTableFunction extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
9
|
const methodName = (0, combi_1.seq)(cds_name_1.CDSName, "=", ">", cds_name_1.CDSName);
|
|
10
|
-
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.str)("DEFINE TABLE FUNCTION"), cds_name_1.CDSName, (0, combi_1.optPrio)(_1.CDSWithParameters), (0, combi_1.str)("RETURNS {"), (0, combi_1.plus)((0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.optPrio)("KEY"), cds_name_1.CDSName, ":", _1.CDSType, ";")), (0, combi_1.str)("} IMPLEMENTED BY METHOD"), methodName, (0, combi_1.opt)(";"));
|
|
10
|
+
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.str)("DEFINE TABLE FUNCTION"), cds_name_1.CDSName, (0, combi_1.optPrio)(_1.CDSWithParameters), (0, combi_1.str)("RETURNS {"), (0, combi_1.plus)((0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.optPrio)("KEY"), cds_name_1.CDSName, ":", _1.CDSType, (0, combi_1.star)(_1.CDSAnnotation), ";")), (0, combi_1.str)("} IMPLEMENTED BY METHOD"), methodName, (0, combi_1.opt)(";"));
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
exports.CDSDefineTableFunction = CDSDefineTableFunction;
|
|
@@ -18,7 +18,7 @@ class CDSElement extends combi_1.Expression {
|
|
|
18
18
|
const funcWithPath = (0, combi_1.seq)(_1.CDSFunction, (0, combi_1.plusPrio)(pathSegment));
|
|
19
19
|
const body = (0, combi_1.altPrio)(extensionWildcard, includeElement, _1.CDSArithmetics, _1.CDSAggregate, _1.CDSString, _1.CDSArithParen, funcWithPath, _1.CDSFunction, cds_cast_1.CDSCast, _1.CDSCase, (0, combi_1.seq)("(", _1.CDSCase, ")"), (0, combi_1.seq)(_1.CDSPrefixedName, (0, combi_1.optPrio)(cds_as_1.CDSAs), (0, combi_1.optPrio)((0, combi_1.altPrio)(redirected, colonThing))), _1.CDSInteger);
|
|
20
20
|
const elementBody = (0, combi_1.altPrio)(typedVirtual, (0, combi_1.seq)((0, combi_1.altPrio)("KEY", "VIRTUAL"), body), body);
|
|
21
|
-
return (0, combi_1.seq)((0, combi_1.starPrio)(_1.CDSAnnotation), elementBody, (0, combi_1.optPrio)(cds_as_1.CDSAs));
|
|
21
|
+
return (0, combi_1.seq)((0, combi_1.starPrio)(_1.CDSAnnotation), elementBody, (0, combi_1.optPrio)(cds_as_1.CDSAs), (0, combi_1.starPrio)(_1.CDSAnnotation));
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.CDSElement = CDSElement;
|
|
@@ -5,7 +5,7 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
5
5
|
class CDSName extends combi_1.Expression {
|
|
6
6
|
getRunnable() {
|
|
7
7
|
const pre = (0, combi_1.seq)("/", (0, combi_1.regex)(/^[\w_]+$/), "/");
|
|
8
|
-
return (0, combi_1.seq)((0, combi_1.optPrio)(":"), (0, combi_1.optPrio)(pre), (0, combi_1.regex)(/^\$?#?[\w_]+$/));
|
|
8
|
+
return (0, combi_1.altPrio)((0, combi_1.regex)(/^"(?:[^"]|"")*"$/), (0, combi_1.seq)((0, combi_1.optPrio)(":"), (0, combi_1.optPrio)(pre), (0, combi_1.regex)(/^\$?#?[\w_]+$/)));
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
exports.CDSName = CDSName;
|
|
@@ -4,7 +4,7 @@ exports.CDSString = void 0;
|
|
|
4
4
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
5
5
|
class CDSString extends combi_1.Expression {
|
|
6
6
|
getRunnable() {
|
|
7
|
-
const reg = (0, combi_1.regex)(/^
|
|
7
|
+
const reg = (0, combi_1.regex)(/^'(?:[^'\\]|''|\\'|\\\\|\\(?!'))*'$/);
|
|
8
8
|
const abapTypeName = (0, combi_1.regex)(/^(?:int[1-9]|int8|sstring|sstr|char|numc|dats|datn|tims|timn|utcl|utclong|fltp|decfloat\d+|dec|string|rawstring|rstr|raw|xstring|clnt|lang|unit|cuky|curr|quan|geom_ewkb|d34n|d16n|d34d|d16d|d34s|d16s|d34r|d16r|d|t|p|n|c|x|f)$/i);
|
|
9
9
|
const abap = (0, combi_1.seq)("abap", ".", abapTypeName, (0, combi_1.optPrio)((0, combi_1.seq)("(", (0, combi_1.regex)(/^\d+$/), ")")), reg);
|
|
10
10
|
return (0, combi_1.altPrio)(abap, reg);
|
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
|
}
|