@abaplint/core 2.120.51 → 2.120.53
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 +6 -0
- package/build/src/abap/1_lexer/lexer.js +18 -3
- package/build/src/abap/4_file_information/abap_file_information_parser.js +5 -2
- package/build/src/abap/5_syntax/expressions/source.js +68 -5
- package/build/src/registry.js +1 -1
- package/build/src/rules/implement_methods.js +5 -0
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -3955,6 +3955,12 @@ declare interface InfoMethodDefinition {
|
|
|
3955
3955
|
isForTesting: boolean;
|
|
3956
3956
|
isAbstract: boolean;
|
|
3957
3957
|
isFinal: boolean;
|
|
3958
|
+
/** "DEFAULT IGNORE", 7.40 SP08: an implementing class need not implement this method,
|
|
3959
|
+
* and a call of it on an unimplemented method does nothing */
|
|
3960
|
+
isDefaultIgnore: boolean;
|
|
3961
|
+
/** "DEFAULT FAIL", 7.40 SP08: an implementing class need not implement this method,
|
|
3962
|
+
* and a call of it raises CX_SY_DYN_CALL_ILLEGAL_METHOD */
|
|
3963
|
+
isDefaultFail: boolean;
|
|
3958
3964
|
visibility: Visibility;
|
|
3959
3965
|
parameters: InfoMethodParameter[];
|
|
3960
3966
|
exceptions: string[];
|
|
@@ -43,6 +43,13 @@ const BUFS = new Set([
|
|
|
43
43
|
CH_DOT, CH_COMMA, CH_COLON, 40 /* ( */, 41 /* ) */, 91 /* [ */,
|
|
44
44
|
93 /* ] */, 43 /* + */, CH_AT
|
|
45
45
|
]);
|
|
46
|
+
// characters that may follow the closing "'", "`" or "|" of a literal, anything else
|
|
47
|
+
// is a syntax error, "There must be a space or equivalent character after ...",
|
|
48
|
+
// note that "(" is allowed, ie. text elements like 'text'(001)
|
|
49
|
+
const AFTER_LITERAL = new Set([
|
|
50
|
+
EOF, CH_SPACE, CH_TAB, CH_NL, CH_DOT, CH_COMMA, CH_COLON, CH_DQUOTE,
|
|
51
|
+
40 /* ( */, 41 /* ) */, 91 /* [ */, 93 /* ] */
|
|
52
|
+
]);
|
|
46
53
|
class Lexer {
|
|
47
54
|
run(file, virtual) {
|
|
48
55
|
this.virtual = virtual;
|
|
@@ -322,7 +329,11 @@ class Lexer {
|
|
|
322
329
|
&& ahead !== CH_BACKTICK
|
|
323
330
|
&& this.buffer.countIsEven(CH_BACKTICK)) {
|
|
324
331
|
// end of ping
|
|
325
|
-
|
|
332
|
+
if (AFTER_LITERAL.has(ahead)) {
|
|
333
|
+
this.add();
|
|
334
|
+
}
|
|
335
|
+
// else: no separator after the literal, keep the buffer so the following
|
|
336
|
+
// characters are glued onto the token, the parser will report the error
|
|
326
337
|
if (ahead === CH_DQUOTE) {
|
|
327
338
|
this.m = ModeComment;
|
|
328
339
|
}
|
|
@@ -335,7 +346,9 @@ class Lexer {
|
|
|
335
346
|
&& (current === CH_PIPE || current === CH_LBRACE)
|
|
336
347
|
&& (stream.prevChar() !== CH_BACKSLASH || (stream.prevPrevChar() === CH_BACKSLASH && stream.prevChar() === CH_BACKSLASH))) {
|
|
337
348
|
// end of template
|
|
338
|
-
|
|
349
|
+
if (current === CH_LBRACE || AFTER_LITERAL.has(ahead)) {
|
|
350
|
+
this.add();
|
|
351
|
+
}
|
|
339
352
|
this.m = ModeNormal;
|
|
340
353
|
}
|
|
341
354
|
else if (this.m === ModeTemplate
|
|
@@ -350,7 +363,9 @@ class Lexer {
|
|
|
350
363
|
&& ahead !== CH_QUOTE
|
|
351
364
|
&& this.buffer.countIsEven(CH_QUOTE)) {
|
|
352
365
|
// end of string
|
|
353
|
-
|
|
366
|
+
if (AFTER_LITERAL.has(ahead)) {
|
|
367
|
+
this.add();
|
|
368
|
+
}
|
|
354
369
|
if (ahead === CH_DQUOTE) {
|
|
355
370
|
this.m = ModeComment;
|
|
356
371
|
}
|
|
@@ -326,12 +326,15 @@ class ABAPFileInformationParser {
|
|
|
326
326
|
continue;
|
|
327
327
|
}
|
|
328
328
|
const parameters = this.parseMethodParameters(def);
|
|
329
|
+
const concatenated = def.concatTokens().toUpperCase();
|
|
329
330
|
methods.push({
|
|
330
331
|
name: methodName.getStr(),
|
|
331
332
|
identifier: new _identifier_1.Identifier(methodName, this.filename),
|
|
332
333
|
isRedefinition: def.findDirectExpression(Expressions.Redefinition) !== undefined,
|
|
333
|
-
isForTesting:
|
|
334
|
-
isFinal:
|
|
334
|
+
isForTesting: concatenated.includes(" FOR TESTING"),
|
|
335
|
+
isFinal: concatenated.includes(" FINAL"),
|
|
336
|
+
isDefaultIgnore: concatenated.includes(" DEFAULT IGNORE"),
|
|
337
|
+
isDefaultFail: concatenated.includes(" DEFAULT FAIL"),
|
|
335
338
|
isAbstract: def.findDirectExpression(Expressions.Abstract) !== undefined,
|
|
336
339
|
isEventHandler: def.findDirectExpression(Expressions.EventHandler) !== undefined,
|
|
337
340
|
visibility,
|
|
@@ -163,6 +163,7 @@ class Source {
|
|
|
163
163
|
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
|
|
164
164
|
}
|
|
165
165
|
this.addIfInferred(node, input, foundType);
|
|
166
|
+
this.traverseRemainingChildren(children, input);
|
|
166
167
|
return foundType;
|
|
167
168
|
}
|
|
168
169
|
case "REF":
|
|
@@ -258,11 +259,15 @@ class Source {
|
|
|
258
259
|
}
|
|
259
260
|
let hexExpected = false;
|
|
260
261
|
let hexNext = false;
|
|
262
|
+
// an arithmetic operator has been seen, so the operands that follow
|
|
263
|
+
// determine a calculation type rather than simply replacing the context
|
|
264
|
+
let arithmetic = false;
|
|
261
265
|
while (children.length >= 0) {
|
|
262
266
|
if (first instanceof nodes_1.ExpressionNode) {
|
|
263
267
|
const get = first.get();
|
|
264
268
|
if (get instanceof Expressions.MethodCallChain) {
|
|
265
|
-
|
|
269
|
+
const found = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
|
|
270
|
+
context = arithmetic === true ? this.infer(context, found, true) : found;
|
|
266
271
|
if (context === undefined) {
|
|
267
272
|
const message = "Method has no RETURNING value";
|
|
268
273
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
@@ -270,18 +275,19 @@ class Source {
|
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
277
|
else if (get instanceof Expressions.FieldChain) {
|
|
273
|
-
|
|
278
|
+
const found = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
|
|
279
|
+
context = arithmetic === true ? this.infer(context, found, true) : found;
|
|
274
280
|
}
|
|
275
281
|
else if (get instanceof Expressions.StringTemplate) {
|
|
276
282
|
context = string_template_1.StringTemplate.runSyntax(first, input);
|
|
277
283
|
}
|
|
278
284
|
else if (get instanceof Expressions.Source) {
|
|
279
285
|
const found = Source.runSyntax(first, input);
|
|
280
|
-
context = this.infer(context, found);
|
|
286
|
+
context = this.infer(context, found, arithmetic);
|
|
281
287
|
}
|
|
282
288
|
else if (get instanceof Expressions.Constant) {
|
|
283
289
|
const found = constant_1.Constant.runSyntax(first);
|
|
284
|
-
context = this.infer(context, found);
|
|
290
|
+
context = this.infer(context, found, arithmetic);
|
|
285
291
|
}
|
|
286
292
|
else if (get instanceof Expressions.Dereference) {
|
|
287
293
|
context = dereference_1.Dereference.runSyntax(first, context, input);
|
|
@@ -290,6 +296,7 @@ class Source {
|
|
|
290
296
|
context = component_chain_1.ComponentChain.runSyntax(context, first, input);
|
|
291
297
|
}
|
|
292
298
|
else if (get instanceof Expressions.ArithOperator) {
|
|
299
|
+
arithmetic = true;
|
|
293
300
|
if (first.concatTokens() === "**") {
|
|
294
301
|
context = new basic_1.FloatType();
|
|
295
302
|
}
|
|
@@ -336,7 +343,13 @@ class Source {
|
|
|
336
343
|
Source.runSyntax(last, input);
|
|
337
344
|
}
|
|
338
345
|
}
|
|
339
|
-
static infer(context, found) {
|
|
346
|
+
static infer(context, found, arithmetic = false) {
|
|
347
|
+
if (arithmetic === true) {
|
|
348
|
+
const calculation = this.calculationType(context, found);
|
|
349
|
+
if (calculation !== undefined) {
|
|
350
|
+
return calculation;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
340
353
|
if (context instanceof basic_1.FloatType && found instanceof basic_1.IntegerType) {
|
|
341
354
|
return context;
|
|
342
355
|
}
|
|
@@ -350,6 +363,56 @@ class Source {
|
|
|
350
363
|
return found;
|
|
351
364
|
}
|
|
352
365
|
}
|
|
366
|
+
/** The calculation type of an arithmetic expression is the most general of the
|
|
367
|
+
* operand types. An operand of type c, n, d, t or string is converted into it and
|
|
368
|
+
* never becomes the result: "lv_f * '0.25'" is a float, not a character field of
|
|
369
|
+
* length four. Returns undefined when neither operand is numeric, leaving the
|
|
370
|
+
* decision to the caller. */
|
|
371
|
+
static calculationType(left, right) {
|
|
372
|
+
if (left === undefined || right === undefined) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
else if (left instanceof basic_1.VoidType || left instanceof unknown_type_1.UnknownType) {
|
|
376
|
+
// not knowing an operand means not knowing the result
|
|
377
|
+
return left;
|
|
378
|
+
}
|
|
379
|
+
else if (right instanceof basic_1.VoidType || right instanceof unknown_type_1.UnknownType) {
|
|
380
|
+
return right;
|
|
381
|
+
}
|
|
382
|
+
const leftRank = this.numericRank(left);
|
|
383
|
+
const rightRank = this.numericRank(right);
|
|
384
|
+
if (leftRank === undefined && rightRank === undefined) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
else if (rightRank === undefined) {
|
|
388
|
+
return left;
|
|
389
|
+
}
|
|
390
|
+
else if (leftRank === undefined) {
|
|
391
|
+
return right;
|
|
392
|
+
}
|
|
393
|
+
return leftRank >= rightRank ? left : right;
|
|
394
|
+
}
|
|
395
|
+
static numericRank(type) {
|
|
396
|
+
if (type instanceof basic_1.DecFloat34Type) {
|
|
397
|
+
return 6;
|
|
398
|
+
}
|
|
399
|
+
else if (type instanceof basic_1.DecFloat16Type) {
|
|
400
|
+
return 5;
|
|
401
|
+
}
|
|
402
|
+
else if (type instanceof basic_1.FloatType) {
|
|
403
|
+
return 4;
|
|
404
|
+
}
|
|
405
|
+
else if (type instanceof basic_1.PackedType) {
|
|
406
|
+
return 3;
|
|
407
|
+
}
|
|
408
|
+
else if (type instanceof basic_1.Integer8Type) {
|
|
409
|
+
return 2;
|
|
410
|
+
}
|
|
411
|
+
else if (type instanceof basic_1.IntegerType) {
|
|
412
|
+
return 1;
|
|
413
|
+
}
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
353
416
|
static addIfInferred(node, input, inferredType) {
|
|
354
417
|
const basic = new basic_types_1.BasicTypes(input);
|
|
355
418
|
const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
|
package/build/src/registry.js
CHANGED
|
@@ -221,6 +221,11 @@ class ImplementMethods extends _abap_rule_1.ABAPRule {
|
|
|
221
221
|
return [idef];
|
|
222
222
|
}
|
|
223
223
|
for (const m of this.findInterfaceMethods(idef)) {
|
|
224
|
+
if (m.method.isDefaultIgnore === true || m.method.isDefaultFail === true) {
|
|
225
|
+
// the interface says an implementation is optional, so asking for one
|
|
226
|
+
// reports an error where ABAP reports none
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
224
229
|
if (this.isAbstract(m, interfaceInfo, def)) {
|
|
225
230
|
if (def.isAbstract) {
|
|
226
231
|
continue;
|