@abaplint/core 2.120.52 → 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.
@@ -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[];
@@ -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: def.concatTokens().toUpperCase().includes(" FOR TESTING"),
334
- isFinal: def.concatTokens().toUpperCase().includes(" FINAL"),
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,
@@ -259,11 +259,15 @@ class Source {
259
259
  }
260
260
  let hexExpected = false;
261
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;
262
265
  while (children.length >= 0) {
263
266
  if (first instanceof nodes_1.ExpressionNode) {
264
267
  const get = first.get();
265
268
  if (get instanceof Expressions.MethodCallChain) {
266
- context = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
269
+ const found = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
270
+ context = arithmetic === true ? this.infer(context, found, true) : found;
267
271
  if (context === undefined) {
268
272
  const message = "Method has no RETURNING value";
269
273
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
@@ -271,18 +275,19 @@ class Source {
271
275
  }
272
276
  }
273
277
  else if (get instanceof Expressions.FieldChain) {
274
- context = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
278
+ const found = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
279
+ context = arithmetic === true ? this.infer(context, found, true) : found;
275
280
  }
276
281
  else if (get instanceof Expressions.StringTemplate) {
277
282
  context = string_template_1.StringTemplate.runSyntax(first, input);
278
283
  }
279
284
  else if (get instanceof Expressions.Source) {
280
285
  const found = Source.runSyntax(first, input);
281
- context = this.infer(context, found);
286
+ context = this.infer(context, found, arithmetic);
282
287
  }
283
288
  else if (get instanceof Expressions.Constant) {
284
289
  const found = constant_1.Constant.runSyntax(first);
285
- context = this.infer(context, found);
290
+ context = this.infer(context, found, arithmetic);
286
291
  }
287
292
  else if (get instanceof Expressions.Dereference) {
288
293
  context = dereference_1.Dereference.runSyntax(first, context, input);
@@ -291,6 +296,7 @@ class Source {
291
296
  context = component_chain_1.ComponentChain.runSyntax(context, first, input);
292
297
  }
293
298
  else if (get instanceof Expressions.ArithOperator) {
299
+ arithmetic = true;
294
300
  if (first.concatTokens() === "**") {
295
301
  context = new basic_1.FloatType();
296
302
  }
@@ -337,7 +343,13 @@ class Source {
337
343
  Source.runSyntax(last, input);
338
344
  }
339
345
  }
340
- 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
+ }
341
353
  if (context instanceof basic_1.FloatType && found instanceof basic_1.IntegerType) {
342
354
  return context;
343
355
  }
@@ -351,6 +363,56 @@ class Source {
351
363
  return found;
352
364
  }
353
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
+ }
354
416
  static addIfInferred(node, input, inferredType) {
355
417
  const basic = new basic_types_1.BasicTypes(input);
356
418
  const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.52";
78
+ return "2.120.53";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.52",
3
+ "version": "2.120.53",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",